Kinetica   C#   API  Version 7.2.3.1
KineticaType.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text.RegularExpressions;
5 using Avro;
6 using Newtonsoft.Json.Linq;
7 
8 namespace kinetica;
9 
10 public class KineticaType
11  {
12  public class Column
13  {
14  public enum ColumnType
15  {
16  BYTES = (int)Avro.Schema.Type.Bytes,
17  DOUBLE = (int)Avro.Schema.Type.Double,
18  FLOAT = (int)Avro.Schema.Type.Float,
19  INT = (int)Avro.Schema.Type.Int,
20  LONG = (int)Avro.Schema.Type.Long,
21  STRING = (int)Avro.Schema.Type.String,
22  BOOLEAN = (int)Avro.Schema.Type.Boolean,
23  DEFAULT = (int)Avro.Schema.Type.Error
24  };
25 
29  public const int DEFAULT_DECIMAL_PRECISION = 18;
30 
34  public const int DEFAULT_DECIMAL_SCALE = 4;
35 
39  public const int DECIMAL8_MAX_PRECISION = 18;
40 
44  private static readonly Regex DecimalPattern = new Regex(
45  @"decimal\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)",
46  RegexOptions.IgnoreCase | RegexOptions.Compiled);
47 
48  private string m_name;
49  private ColumnType m_type;
50  private bool m_isNullable;
51  private IList<string> m_properties;
52  private int m_precision;
53  private int m_scale;
54  private bool m_isDecimal;
55 
62  public Column(string name, ColumnType type, IList<string>? properties = null)
63  {
64  m_name = name;
65  m_type = type;
66  m_isNullable = false;
67  m_properties = properties ?? [];
68 
69  Initialize();
70  }
71 
76  public string getName() { return m_name; }
77 
82  public ColumnType getType() { return m_type; }
83 
88  public bool isNullable() { return m_isNullable; }
89 
94  public IList<string> getProperties() { return m_properties; }
95 
100  public bool isDecimal() { return m_isDecimal; }
101 
107  public int getDecimalPrecision() { return m_precision; }
108 
114  public int getDecimalScale() { return m_scale; }
115 
121  public int getDecimalByteSize() { return m_precision > DECIMAL8_MAX_PRECISION ? 12 : 8; }
122 
123  internal void setIsNullable( bool val ) { m_isNullable = val; }
124 
129  public string getTypeString()
130  {
131  return m_type switch
132  {
133  ColumnType.BYTES => "bytes",
134  ColumnType.DOUBLE => "double",
135  ColumnType.FLOAT => "float",
136  ColumnType.INT => "int",
137  ColumnType.LONG => "long",
138  ColumnType.STRING => "string",
139  _ => throw new KineticaException("Unsupported column type: " + m_type),
140  };
141  } // end getTypeString()
142 
143  private void Initialize()
144  {
145  if (string.IsNullOrEmpty(m_name))
146  {
147  throw new ArgumentException("Name must not be empty.");
148  }
149 
150  switch (m_type)
151  {
152  case ColumnType.BYTES:
153  case ColumnType.DOUBLE:
154  case ColumnType.FLOAT:
155  case ColumnType.INT:
156  case ColumnType.LONG:
157  case ColumnType.STRING:
158  case ColumnType.BOOLEAN:
159  break;
160 
161  default:
162  throw new ArgumentException($"Column {m_name} must be of type BYTES, DOUBLE, FLOAT, INT, LONG, STRING or BOOLEAN.");
163  }
164 
165  // Initialize decimal info with defaults
166  m_precision = DEFAULT_DECIMAL_PRECISION;
167  m_scale = DEFAULT_DECIMAL_SCALE;
168  m_isDecimal = false;
169 
170  foreach (var it in m_properties)
171  {
172  if (string.IsNullOrEmpty(it))
173  {
174  throw new ArgumentException("Properties must not be empty.");
175  }
176 
177  if (!m_isNullable && (it == ColumnProperty.NULLABLE))
178  {
179  m_isNullable = true;
180  }
181 
182  // Check for decimal property and extract precision/scale
183  if (!m_isDecimal)
184  {
185  SetDecimalInfo(it);
186  }
187  }
188  }
189 
195  private void SetDecimalInfo(string property)
196  {
197  // Check for simple "decimal" property
198  if (property.Equals(ColumnProperty.DECIMAL, StringComparison.OrdinalIgnoreCase))
199  {
200  m_isDecimal = true;
201  // Use defaults already set
202  return;
203  }
204 
205  // Check for decimal(precision, scale) format
206  var match = DecimalPattern.Match(property);
207  if (match.Success)
208  {
209  m_isDecimal = true;
210  if (int.TryParse(match.Groups[1].Value, out int precision))
211  m_precision = precision;
212  if (int.TryParse(match.Groups[2].Value, out int scale))
213  m_scale = scale;
214  }
215  }
216 
217  public override string ToString()
218  {
219  return $"{m_name} ({m_type})";
220  }
221  } // end class Column
222 
223  private class TypeData
224  {
225  public string? label;
226  public IList<Column> columns = [];
227  public Dictionary<string, int> columnMap = [];
228  public string? schemaString = null;
229  public Schema? schema = null;
230  public Type? sourceType = null;
231  }
232 
233  private TypeData m_data = new();
234  private IDictionary<string, IList<string>> m_properties = new Dictionary<string, IList<string>>();
235  private string? m_typeId = null;
236 
243  public static KineticaType fromTable(Kinetica kinetica, string tableName)
244  {
245  var response = kinetica.showTable(tableName);
246  var typeIdCount = response.type_ids.Count;
247 
248  if (typeIdCount == 0)
249  {
250  throw new KineticaException($"Table {tableName} does not exist.");
251  }
252 
253  string typeId = response.type_ids[0];
254  if (typeIdCount > 1)
255  {
256  for (int i = 1; i < typeIdCount; ++i)
257  {
258  if (response.type_ids[i] != typeId)
259  {
260  throw new KineticaException("Table {tableName} is not homogeneous.");
261  }
262  }
263  }
264 
265  return new KineticaType(response.type_labels[0], response.type_schemas[0], response.properties[0], typeId );
266  }
267 
274  public static KineticaType fromTypeID(Kinetica kinetica, string typeId)
275  {
276  var response = kinetica.showTypes(typeId, "");
277 
278  if (response.type_ids.Count < 1)
279  {
280  throw new KineticaException($"Type {typeId} does not exist.");
281  }
282 
283  return new KineticaType(response.labels[0], response.type_schemas[0], response.properties[0]);
284  }
285 
286 
294  public static KineticaType fromDynamicSchema( string dynamicTableSchemaString,
295  Object[] columnHeaders, Object[] columnTypes )
296  {
297  // Make sure that the lists of column names and types are of the same length
298  if ( columnHeaders.Length != columnTypes.Length )
299  throw new KineticaException($"List of column names ({columnHeaders.Length}) and types ({columnTypes.Length}) are not of the same length." );
300 
301  // Parse the schema string so that we can later check if a given column is nullable
302  JObject dynamicSchemaJson;
303  try
304  {
305  dynamicSchemaJson = JObject.Parse( dynamicTableSchemaString );
306  }
307  catch ( Exception ex )
308  {
309  throw new KineticaException( ex.ToString() );
310  }
311 
312  // Create a delegate for checking if a field/column is nullable
313  // ------------------------------------------------------------
314  // The first parameter is the column name, the second is the JSON object
315  var isColumnNullable = new Func<string, JObject, bool>( (columnName, schemaJson) => {
316  // Find the appropriate field
317  bool foundField = false;
318  JToken? fieldList = schemaJson["fields"];
319  if (fieldList != null)
320  {
321  foreach (var field in fieldList)
322  {
323  if ((string?)field["name"] == columnName)
324  {
325  foundField = true; // found it!
326  // Get the type and see if it's a nullable type
327  // (each field is an array of the column type; so need
328  // to extract the type element first)
329  JToken? fieldType = field["type"];
330  if (fieldType != null)
331  {
332  var typeElement = fieldType["items"];
333  if (typeElement == null || typeElement is JValue) // not an array, so can't be nullable
334  return false;
335  // If the type is an array and the second value is 'null', then it's a nullable
336  if ((typeElement is JArray) && ((string)(typeElement as JArray)[1] == "null"))
337  return true;
338  return false;
339  }
340  } // end if
341  } // end foreach
342  }
343  if ( !foundField )
344  throw new KineticaException( $"Could not find the field named '{columnName}'" );
345  return false; // shouldn't ever get here
346  } );
347 
348  // Create appropriate columns and column properties
349  // ------------------------------------------------
350  List<Column> columns = [];
351  Dictionary<string, IList<string>> columnProperties = [];
352  for ( int i = 0; i < columnHeaders.Length; ++i )
353  {
354  // Get the column's name
355  string column_name = (string)columnHeaders[i];
356 
357  // Get the column's type in string format, which might be a property and not a primitive type
358  // (if so, then we'll have to infer the primitive type and save the property)
359  string columnTypeString = (string)columnTypes[i];
360 
361  // Need to create a list for the properties of the column (we'll
362  // extract at most one from the column type)
363  List<string> columnProperty = [];
364 
365  // We need to also infer the primitive type for this column
366  Column.ColumnType columnType;
367 
368  // Infer the type and property from the given 'type'
369  switch ( columnTypeString )
370  {
371  // Primitive type string (and all properties based on it)
372  case "string":
373  columnType = Column.ColumnType.STRING;
374  break;
375 
376  // All properties allowed for the primitive string type
377  case ColumnProperty.CHAR1:
378  case ColumnProperty.CHAR2:
379  case ColumnProperty.CHAR4:
380  case ColumnProperty.CHAR8:
381  case ColumnProperty.CHAR16:
382  case ColumnProperty.CHAR32:
383  case ColumnProperty.CHAR64:
384  case ColumnProperty.CHAR128:
385  case ColumnProperty.CHAR256:
386  case ColumnProperty.DATE:
388  case ColumnProperty.DECIMAL:
389  case ColumnProperty.IPV4:
390  case ColumnProperty.TIME:
391  case ColumnProperty.ULONG:
392  case ColumnProperty.UUID:
393  case ColumnProperty.JSON:
394  case ColumnProperty.WKT:
395  columnType = Column.ColumnType.STRING;
396  columnProperty.Add( columnTypeString );
397  break;
398 
399  // Primitive type integer
400  case "int":
401  columnType = Column.ColumnType.INT;
402  break;
403 
404  // Properties allowed for the primitive integer type
405  case ColumnProperty.INT8:
406  case ColumnProperty.INT16:
407  case ColumnProperty.BOOLEAN:
408  columnType = Column.ColumnType.INT;
409  columnProperty.Add( columnTypeString );
410  break;
411 
412  // Primitive type long
413  case "long":
414  columnType = Column.ColumnType.LONG;
415  break;
416 
417  // Properties allowed for the long type
419  columnType = Column.ColumnType.LONG;
420  columnProperty.Add( columnTypeString );
421  break;
422 
423  // Primitive type float
424  case "float":
425  columnType = Column.ColumnType.FLOAT;
426  break;
427 
428  // Primitive type double
429  case "double":
430  columnType = Column.ColumnType.DOUBLE;
431  break;
432 
433  // Primitive type bytes
434  case "bytes":
435  columnType = Column.ColumnType.BYTES;
436  break;
437 
438  default:
439  // Handle array types (array(int), array(double,10), etc.)
440  if (columnTypeString.StartsWith(ColumnProperty.ARRAY, StringComparison.OrdinalIgnoreCase))
441  {
442  columnType = Column.ColumnType.STRING;
443  columnProperty.Add(columnTypeString);
444  }
445  // Handle vector types (vector(1024), etc.)
446  else if (columnTypeString.StartsWith(ColumnProperty.VECTOR, StringComparison.OrdinalIgnoreCase))
447  {
448  columnType = Column.ColumnType.BYTES;
449  columnProperty.Add(columnTypeString);
450  }
451  // Handle decimal types with precision/scale (decimal(18,4), etc.)
452  else if (columnTypeString.StartsWith(ColumnProperty.DECIMAL, StringComparison.OrdinalIgnoreCase))
453  {
454  columnType = Column.ColumnType.STRING;
455  columnProperty.Add(columnTypeString);
456  }
457  else
458  {
459  throw new KineticaException($"Unknown data type/property: {columnTypeString}");
460  }
461  break;
462  } // end switch
463 
464  // Check if the column is nullable (where the column name is "column_#" as returned by Kinetica)
465  if ( isColumnNullable( $"column_{i + 1}", dynamicSchemaJson ) )
466  columnProperty.Add( ColumnProperty.NULLABLE );
467 
468  // Now that we have the name, the type and potentially a property for the column,
469  // create a Column type and add it to the list
470  Column column = new( column_name, columnType, columnProperty );
471  columns.Add( column );
472 
473  // Also, save the column property in the column name->property map
474  columnProperties.Add( column_name, columnProperty );
475  } // end looping over column headers and types
476 
477 
478  // Create and return the KineticaType object based on the columns and properties
479  return new KineticaType( "", columns, columnProperties );
480  } // end fromDynamicSchema()
481 
482 
483 
499  public static KineticaType fromClass( Type recordClass, IDictionary<string, IList<string>> properties = null )
500  {
501  return fromClass( recordClass, "", properties );
502  } // end fromClass()
503 
504 
514  public static KineticaType fromClass( Type recordClass, string label, IDictionary<string, IList<string>>? properties = null )
515  {
516  // Get the fields in order (******skipping properties inherited from base classes******)
517  // (fields only from this type, i.e. do not include any inherited fields), and public types only
518  System.Reflection.PropertyInfo[] typeProperties = recordClass.GetProperties( System.Reflection.BindingFlags.DeclaredOnly |
519  System.Reflection.BindingFlags.Instance |
520  System.Reflection.BindingFlags.Public );
521 
522  Array.Sort( typeProperties, (p1, p2) => p1.MetadataToken.CompareTo( p2.MetadataToken ) );
523 
524  // Need to have a list of columns
525  List<Column> columns = [];
526  List<string> columnNames = [];
527 
528  // Per property, check that it is one of: int, long, float, double, string, bytes
529  foreach ( var typeProperty in typeProperties )
530  {
531  string columnName = "";
532  Column.ColumnType columnType = Column.ColumnType.DEFAULT;
533  IList<string>? columnProperties = null;
534  bool isColumnNullable = false;
535 
536  // Get the column name
537  columnName = typeProperty.Name;
538 
539  Type? propertyType = typeProperty.PropertyType;
540 
541  // Check if the field is nullable (declared as T? or Nullable<T>)
542  if ( typeProperty.PropertyType.IsGenericType &&
543  ( typeProperty.PropertyType.GetGenericTypeDefinition() == typeof( Nullable<> ) ) )
544  { // the field is a nullable field
545  isColumnNullable = true;
546  // Change the property type to be the underlying type
547  propertyType = Nullable.GetUnderlyingType(propertyType);
548  }
549 
550  // Check the column data type (must be one of int, long, float, double, string, and bytes)
551  if ( propertyType == typeof( System.String ) )
552  {
553  columnType = Column.ColumnType.STRING;
554  }
555  else if ( propertyType == typeof( System.Int32 ) )
556  {
557  columnType = Column.ColumnType.INT;
558  }
559  else if ( propertyType == typeof( System.Int64 ) )
560  {
561  columnType = Column.ColumnType.LONG;
562  }
563  else if ( propertyType == typeof( float ) )
564  {
565  columnType = Column.ColumnType.FLOAT;
566  }
567  else if ( propertyType == typeof( double ) )
568  {
569  columnType = Column.ColumnType.DOUBLE;
570  }
571  else if ( propertyType == typeof( byte ) )
572  {
573  columnType = Column.ColumnType.BYTES;
574  }
575  else
576  throw new KineticaException( "Unsupported data type for " + propertyType?.Name +
577  ": " + propertyType +
578  " (must be one of int, long, float, double, string, and byte)" );
579 
580  // Extract the given column's properties, if any
581  properties?.TryGetValue(columnName, out columnProperties);
582 
583  // Keep a list of the column names for checking the properties
584  columnNames.Add( columnName );
585 
586  // Create the column
587  Column column = new( columnName, columnType, columnProperties );
588  if ( isColumnNullable ) // Set the appropriate nullable flag for the column
589  column.setIsNullable( true );
590 
591  // Save the column
592  columns.Add( column );
593  } // end looping over all members of the class type
594 
595  // Check for extraneous properties
596  if (properties != null)
597  {
598  IEnumerable<string> propertyKeys = properties.Keys;
599  var unknownColumns = propertyKeys.Where(e => !columnNames.Contains(e));
600  // Check if any property is provided for wrong/non-existing columns
601  if (unknownColumns.Any())
602  throw new KineticaException("Properties specified for unknown columns.");
603  }
604  else properties = new Dictionary<string, IList<string>>();
605 
606  // Create the kinetica type
607  KineticaType kType = new(label, columns, properties);
608 
609  // Save the class information in the type
610  kType.saveSourceType( recordClass );
611 
612  return kType;
613  } // end fromClass()
614 
615 
624  public static KineticaType fromObject( Object recordObj, IDictionary<string, IList<string>> properties = null )
625  {
626  return fromObject( recordObj, "", properties );
627  } // end fromObject()
628 
629 
639  public static KineticaType fromObject(Object recordObj, string label = "", IDictionary<string, IList<string>> properties = null)
640  {
641  // Create the type schema from the object
642  // --------------------------------------
643  // Get the class type
644  Type object_type = recordObj.GetType();
645 
646  return fromClass( object_type, label, properties );
647  } // end fromObject()
648 
653  public KineticaType(IList<Column> columns)
654  {
655  m_data.columns = columns;
656  Initialize();
657  CreateSchema(); // create the schema from columns
658  }
659 
665  public KineticaType(string label, IList<Column> columns) : this(columns)
666  {
667  m_data.label = label;
668  }
669 
676  public KineticaType( string label, IList<Column> columns, IDictionary<string, IList<string>> properties ) : this( label, columns )
677  {
678  m_properties = properties ?? new Dictionary<string, IList<string>>();
679  }
680 
685  public KineticaType(string typeSchema)
686  {
687  m_data.schemaString = typeSchema;
688  CreateSchemaFromString( typeSchema );
689  CreateSchema();
690  }
691 
699  public KineticaType(string label, string typeSchema, IDictionary<string, IList<string>> properties, string? typeId = null )
700  {
701  m_properties = properties;
702  m_typeId = typeId;
703  m_data.label = label;
704  m_data.schemaString = typeSchema;
705  CreateSchemaFromString(typeSchema, properties);
706  CreateSchema();
707  }
708 
709  public string getLabel() { return m_data.label; }
710  public IList<Column> getColumns() { return m_data.columns; }
711  public Column getColumn(int index) { return m_data.columns[index]; }
712  public Column getColumn(string name) { return m_data.columns[getColumnIndex(name)]; }
713  public int getColumnCount() { return m_data.columns.Count; }
714  public int getColumnIndex(string name) { return m_data.columnMap[name]; }
715  public bool hasColumn(string name) { return m_data.columnMap.ContainsKey(name); }
716  public Schema getSchema() { return m_data.schema; }
717  public Type? getSourceType() { return m_data.sourceType;}
718  public string getSchemaString() { return m_data.schemaString; }
719  public string getTypeID() { return m_typeId; }
720 
725  public void saveSourceType( Type sourceType )
726  {
727  this.m_data.sourceType = sourceType;
728  } // end saveSourceType
729 
730 
737  public string create(Kinetica kinetica)
738  {
739  // Save the association between this KineticaType's source and itself in the Kinetica object
740  // for future reference (it helps with encoding and decoding records)
741  if ( this.m_data.sourceType != null )
742  kinetica.SetKineticaSourceClassToTypeMapping( this.m_data.sourceType, this );
743 
744  // Register the type with Kinetica
745  CreateTypeResponse response = kinetica.createType( m_data.schemaString, m_data.label, m_properties);
746  return response.type_id;
747  } // end create()
748 
749  private KineticaType() { }
750 
755  private void Initialize()
756  {
757  int columnCount = m_data.columns.Count;
758 
759  if (columnCount == 0)
760  {
761  throw new ArgumentException("At least one column must be specified.");
762  }
763 
764  for (int i = 0; i < columnCount; ++i)
765  {
766  string columnName = m_data.columns[i].getName();
767 
768  if (m_data.columnMap.ContainsKey(columnName))
769  {
770  throw new ArgumentException("Duplicate column name " + columnName + " specified.");
771  }
772 
773  m_data.columnMap[columnName] = i;
774  }
775  } // end Initialize()
776 
777 
783  private void CreateSchemaFromString( string typeSchema,
784  IDictionary<string, IList<string>> properties = null)
785  {
786  // Create the avro schema from the string and save it
787  try
788  {
789  m_data.schema = RecordSchema.Parse(KineticaData.NormalizeSchemaJson(typeSchema));
790  }
791  catch (Exception ex)
792  {
793  throw new KineticaException(ex.ToString());
794  }
795 
796  var root = JObject.Parse(typeSchema);
797 
798  var rootType = root["type"];
799  if ((null == rootType) || !rootType.ToString().Contains("record"))
800  {
801  throw new ArgumentException("Schema must be of type record.");
802  }
803 
804  var fields = root["fields"];
805  if ((null == fields) || !fields.HasValues)
806  {
807  throw new ArgumentException("Schema has no fields.");
808  }
809 
810  foreach (var field in fields)
811  {
812  //if (!field->first.empty() || field->second.empty())
813  //{
814  // throw std::invalid_argument("Schema has invalid field.");
815  //}
816 
817  // Do NOT use ToString 'cause it includes the double quotes (turns it into a JSON representation)
818  string? fieldName = (string?)field["name"];
819  if (string.IsNullOrEmpty(fieldName))
820  {
821  throw new ArgumentException("Schema has unnamed field.");
822  }
823 
824  if (m_data.columnMap.ContainsKey(fieldName))
825  {
826  throw new ArgumentException($"Duplicate field name {fieldName}.");
827  }
828 
829  var fieldType = field["type"];
830  if (null == fieldType)
831  {
832  throw new ArgumentException($"Field {fieldName} has no type.");
833  }
834 
835  // Flag for nullability
836  bool isColumnNullable = false;
837 
838  if (fieldType.HasValues) // If it has children
839  {
840  var fieldTypeArray = fieldType;
841 
842  foreach (var fieldTypeElement in fieldTypeArray.Children())
843  {
844  bool valid = false;
845  //if (fieldTypeElement->first.empty())
846  {
847  var fieldTypeElementString = fieldTypeElement.ToString();
848 
849  if (!string.IsNullOrEmpty(fieldTypeElementString))
850  {
851  if (fieldTypeElementString == "null" || fieldTypeElementString == "\"null\"")
852  {
853  isColumnNullable = true;
854  valid = true;
855  }
856  else //if (fieldType->empty())
857  {
858  fieldType = fieldTypeElement; // fieldTypeElementString;
859  valid = true;
860  }
861  }
862  }
863 
864  if (!valid)
865  {
866  throw new ArgumentException("Field {fieldName} has invalid type.");
867  }
868  }
869  }
870 
871  Column.ColumnType columnType;
872 
873  if (fieldType.ToString().Equals("bytes") || fieldType.ToString().Equals("\"bytes\""))
874  {
875  columnType = Column.ColumnType.BYTES;
876  }
877  else if (fieldType.ToString().Equals("double") || fieldType.ToString().Equals("\"double\""))
878  {
879  columnType = Column.ColumnType.DOUBLE;
880  }
881  else if (fieldType.ToString().Equals("float") || fieldType.ToString().Equals("\"float\""))
882  {
883  columnType = Column.ColumnType.FLOAT;
884  }
885  else if (fieldType.ToString().Equals("int") || fieldType.ToString().Equals("\"int\""))
886  {
887  columnType = Column.ColumnType.INT;
888  }
889  else if (fieldType.ToString().Equals("long") || fieldType.ToString().Equals("\"long\""))
890  {
891  columnType = Column.ColumnType.LONG;
892  }
893  else if (fieldType.ToString().Equals("string") || fieldType.ToString().Equals("\"string\""))
894  {
895  columnType = Column.ColumnType.STRING;
896  }
897  else if (fieldType.ToString().Equals("boolean") || fieldType.ToString().Equals("\"boolean\""))
898  {
899  columnType = Column.ColumnType.BOOLEAN;
900  }
901  else
902  {
903  throw new ArgumentException("Field {fieldName} must be of type bytes, double, float, int, long, string or boolean.");
904  }
905 
906  IList<string>? columnProperties = null;
907  properties?.TryGetValue(fieldName, out columnProperties);
908  // Check the column properties for nullability
909  if ( ( null != columnProperties ) &&
910  ( columnProperties.Contains( ColumnProperty.NULLABLE ) ) )
911  isColumnNullable = true;
912 
913  // Create the column to be added
914  Column column = new( fieldName, columnType, columnProperties );
915 
916  column.setIsNullable( isColumnNullable );
917 
918  m_data.columns.Add( column );
919 
920  m_data.columnMap[fieldName] = m_data.columns.Count - 1;
921  }
922  } // end CreateSchemaFromString()
923 
927  private void CreateSchema()
928  {
929  // First, check if the schema has already been created
930  if (m_data.schema != null)
931  {
932  // nothing to do
933  return;
934  }
935 
936  // Check if the schema string exists, if so, create the schema from that
937  if (m_data.schemaString != null)
938  {
939  try
940  {
941  m_data.schema = RecordSchema.Parse(KineticaData.NormalizeSchemaJson(m_data.schemaString));
942  return;
943  }
944  catch (Exception ex)
945  {
946  throw new KineticaException(ex.ToString());
947  }
948  } // done creating the schema from the schema string
949 
950  // Since the shortcuts didn't apply, create a JSON object from the columns
951  // and then create the schema and the schema string off it
952  // --------------------------------------------------------------------------
953  // Create the json string for the type
954  string schemaString = "";
955  // Create the json string opening with empty fields (with a generic 'type_name' (because the
956  // server always replaces the name with this string anyway) )
957  string schemaOpening = "{'type':'record','name':'type_name','fields':[";
958  // Create the json string closing
959  string schemaClosing = "]}";
960 
961  schemaString += schemaOpening;
962 
963  // Create the json substrings for the columns
964  foreach (var column in m_data.columns)
965  {
966  // Add the name
967  string fieldName = ("'name':'" + column.getName() + "'");
968 
969  // Add the type
970  string fieldType = "";
971  if (column.isNullable())
972  { // the column is nullable, so we need a union
973  fieldType = ("['" + column.getTypeString() + "','null']");
974  }
975  else // regular type, no union needed
976  {
977  fieldType = ( "'" + column.getTypeString() + "'" );
978  }
979  fieldType = ("'type':" + fieldType);
980 
981  // Put the field together
982  string field = ("{" + fieldName + "," + fieldType + "},");
983  schemaString += field;
984  } // end looping over the fields
985 
986  // Trim the trailing comma from the fields
987  char[] comma = [','];
988  schemaString = schemaString.TrimEnd(comma);
989  // Add the ending of the json string
990  schemaString += schemaClosing;
991 
992  // Create the RecordSchema from the JSON string
993  try
994  {
995  m_data.schema = RecordSchema.Parse(KineticaData.NormalizeSchemaJson(schemaString));
996  }
997  catch (Exception ex)
998  {
999  throw new KineticaException(ex.ToString());
1000  }
1001 
1002  // Save the schema string
1003  m_data.schemaString = m_data.schema.ToString();
1004  return;
1005  } // end CreateSchema()
1006  } // end class KineticaType
const string CHAR64
This property provides optimized memory, disk and query performance for string columns.
string type_id
An identifier representing the created type.
Definition: CreateType.cs:939
int getColumnCount()
const string INT16
This property provides optimized memory and query performance for int columns.
string getSchemaString()
Class for record schemas
Definition: RecordSchema.cs:31
bool isDecimal()
Returns whether this column is a decimal type.
const string CHAR128
This property provides optimized memory, disk and query performance for string columns.
KineticaType(string label, IList< Column > columns, IDictionary< string, IList< string >> properties)
Create a KineticaType object with the given column, label, and property information.
static KineticaType fromClass(Type recordClass, IDictionary< string, IList< string >> properties=null)
Create a KineticaType object from properties of a record class and Kinetica column properties.
ColumnType getType()
Returns the enumeration for the column type.
Definition: KineticaType.cs:82
static KineticaType fromTypeID(Kinetica kinetica, string typeId)
Create a KineticaType object based on an existing type in the database.
const string CHAR1
This property provides optimized memory, disk and query performance for string columns.
const string TIMESTAMP
Valid only for 'long' columns.
int getDecimalScale()
Returns the scale for decimal columns.
Base class for all schema types
Definition: Schema.cs:29
KineticaData - class to help with Avro Encoding for Kinetica
Definition: KineticaData.cs:14
const int DEFAULT_DECIMAL_SCALE
Default scale for decimal columns (matches Java API).
Definition: KineticaType.cs:34
const string WKT
Valid only for 'string' and 'bytes' columns.
bool isNullable()
Returns if the column is nullable.
Definition: KineticaType.cs:88
const int DEFAULT_DECIMAL_PRECISION
Default precision for decimal columns (matches Java API).
Definition: KineticaType.cs:29
const string TIME
Valid only for 'string' columns.
const string DECIMAL
Valid only for 'string' columns.
const string IPV4
This property provides optimized memory, disk and query performance for string columns representing I...
const string VECTOR
Valid only for 'bytes' columns.
const string CHAR2
This property provides optimized memory, disk and query performance for string columns.
const string CHAR8
This property provides optimized memory, disk and query performance for string columns.
const string ULONG
Valid only for 'string' columns.
const string CHAR32
This property provides optimized memory, disk and query performance for string columns.
const string CHAR256
This property provides optimized memory, disk and query performance for string columns.
Type
Enum for schema types
Definition: Schema.cs:34
Column getColumn(string name)
const string BOOLEAN
This property provides optimized memory and query performance for int columns.
string getTypeString()
Returns the string format of the data type.
IList< string > getProperties()
Returns the properties for the column.
Definition: KineticaType.cs:94
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
static KineticaType fromObject(Object recordObj, IDictionary< string, IList< string >> properties=null)
Create a KineticaType object from properties of a record object and Kinetica column properties.
Type? getSourceType()
Column properties used for Kinetica types.
const string UUID
Valid only for 'string' columns.
static KineticaType fromTable(Kinetica kinetica, string tableName)
Create a KineticaType object based on an existing table in the database.
void saveSourceType(Type sourceType)
Saves the given type as this KineticaType's source type.
IList< Column > getColumns()
KineticaType(string label, string typeSchema, IDictionary< string, IList< string >> properties, string? typeId=null)
Create a KineticaType object using the string-formatted schema and properties for its columns.
const string ARRAY
Valid only for 'string' columns.
string create(Kinetica kinetica)
Given a handle to the server, creates a type in the database based on this data type.
int getColumnIndex(string name)
const string JSON
Valid only for 'string' columns.
const string DATETIME
Valid only for 'string' columns.
Immutable metadata about a column in a Kinetica type.
Definition: Column.cs:11
Column getColumn(int index)
string getName()
Returns the name of the column.
Definition: KineticaType.cs:76
const string NULLABLE
This property indicates that this column is nullable.
int getDecimalByteSize()
Returns the byte size needed to store decimal values for this column.
Schema getSchema()
bool hasColumn(string name)
const int DECIMAL8_MAX_PRECISION
Maximum precision for 8-byte decimals.
Definition: KineticaType.cs:39
static KineticaType fromClass(Type recordClass, string label, IDictionary< string, IList< string >>? properties=null)
Create a KineticaType object from properties of a record class and Kinetica column properties.
KineticaType(IList< Column > columns)
Create a KineticaType object with the given column information.
KineticaType(string label, IList< Column > columns)
Create a KineticaType object with the given column and label information.
const string INT8
This property provides optimized memory and query performance for int columns.
const string CHAR16
This property provides optimized memory, disk and query performance for string columns.
static KineticaType fromDynamicSchema(string dynamicTableSchemaString, Object[] columnHeaders, Object[] columnTypes)
Create a KineticaType object based on information provided in a dynamic schema.
string getTypeID()
const string CHAR4
This property provides optimized memory, disk and query performance for string columns.
KineticaType(string typeSchema)
Create a KineticaType object using the string-formatted schema for the type.
Column(string name, ColumnType type, IList< string >? properties=null)
Creates a Column object from the given name, type, and properties.
Definition: KineticaType.cs:62
static KineticaType fromObject(Object recordObj, string label="", IDictionary< string, IList< string >> properties=null)
Create a KineticaType object from properties of a record object and Kinetica column properties.
ColumnType ColumnType
Gets the column type.
Definition: Column.cs:57
Immutable collection of metadata about a Kinetica type.
Definition: Type.cs:36
string getLabel()
const string DATE
Valid only for 'string' columns.
override string ToString()
int getDecimalPrecision()
Returns the precision for decimal columns.
static Schema Parse(string json)
Parses a given JSON string to create a new schema object
Definition: Schema.cs:141
A set of results returned by Kinetica.createType.
Definition: CreateType.cs:933