2 using System.Collections.Generic;
5 using Newtonsoft.Json.Linq;
15 BYTES = Avro.EnumSchema.Type.Bytes,
16 DOUBLE = Avro.EnumSchema.Type.Double,
17 FLOAT = Avro.EnumSchema.Type.Float,
18 INT = Avro.EnumSchema.Type.Int,
19 LONG = Avro.EnumSchema.Type.Long,
20 STRING = Avro.EnumSchema.Type.String,
21 DEFAULT = Avro.EnumSchema.Type.Error
24 private string m_name;
25 private ColumnType m_type;
26 private bool m_isNullable;
27 private IList<string> m_properties;
40 m_properties = properties ?? [];
49 public string getName() {
return m_name; }
69 internal void setIsNullable(
bool val ) { m_isNullable = val; }
89 private void Initialize()
91 if (
string.IsNullOrEmpty(m_name))
93 throw new ArgumentException(
"Name must not be empty.");
98 case ColumnType.BYTES:
99 case ColumnType.DOUBLE:
100 case ColumnType.FLOAT:
102 case ColumnType.LONG:
103 case ColumnType.STRING:
107 throw new ArgumentException($
"Column {m_name} must be of type BYTES, DOUBLE, FLOAT, INT, LONG or STRING.");
110 foreach (var it
in m_properties)
112 if (
string.IsNullOrEmpty(it))
114 throw new ArgumentException(
"Properties must not be empty.");
117 if (!m_isNullable && (it == ColumnProperty.NULLABLE))
126 return $
"{m_name} ({m_type})";
130 private class TypeData
132 public string? label;
133 public IList<Column> columns = [];
134 public Dictionary<string, int> columnMap = [];
135 public string? schemaString =
null;
136 public Schema? schema =
null;
137 public Type? sourceType =
null;
140 private TypeData m_data =
new();
141 private IDictionary<string, IList<string>> m_properties =
new Dictionary<string, IList<string>>();
142 private string? m_typeId =
null;
152 var response =
kinetica.showTable(tableName);
153 var typeIdCount = response.type_ids.Count;
155 if (typeIdCount == 0)
160 string typeId = response.type_ids[0];
163 for (
int i = 1; i < typeIdCount; ++i)
165 if (response.type_ids[i] != typeId)
172 return new KineticaType(response.type_labels[0], response.type_schemas[0], response.properties[0], typeId );
183 var response =
kinetica.showTypes(typeId,
"");
185 if (response.type_ids.Count < 1)
190 return new KineticaType(response.labels[0], response.type_schemas[0], response.properties[0]);
202 Object[] columnHeaders, Object[] columnTypes )
205 if ( columnHeaders.Length != columnTypes.Length )
206 throw new KineticaException($
"List of column names ({columnHeaders.Length}) and types ({columnTypes.Length}) are not of the same length." );
209 JObject dynamicSchemaJson;
212 dynamicSchemaJson = JObject.Parse( dynamicTableSchemaString );
214 catch ( Exception ex )
222 var isColumnNullable =
new Func<string, JObject, bool>( (columnName, schemaJson) => {
224 bool foundField =
false;
225 JToken? fieldList = schemaJson[
"fields"];
226 if (fieldList !=
null)
228 foreach (var field
in fieldList)
230 if ((
string?)field[
"name"] == columnName)
236 JToken? fieldType = field[
"type"];
237 if (fieldType !=
null)
239 var typeElement = fieldType[
"items"];
240 if (typeElement ==
null || typeElement is JValue)
243 if ((typeElement is JArray) && ((
string)(typeElement as JArray)[1] ==
"null"))
257 List<Column> columns = [];
258 Dictionary<string, IList<string>> columnProperties = [];
259 for (
int i = 0; i < columnHeaders.Length; ++i )
262 string column_name = (string)columnHeaders[i];
266 string columnTypeString = (string)columnTypes[i];
270 List<string> columnProperty = [];
276 switch ( columnTypeString )
299 columnProperty.Add( columnTypeString );
311 columnProperty.Add( columnTypeString );
322 columnProperty.Add( columnTypeString );
345 if ( isColumnNullable( $
"column_{i + 1}", dynamicSchemaJson ) )
350 Column column =
new( column_name, columnType, columnProperty );
351 columns.Add( column );
354 columnProperties.Add( column_name, columnProperty );
359 return new KineticaType(
"", columns, columnProperties );
381 return fromClass( recordClass,
"", properties );
394 public static KineticaType fromClass( Type recordClass,
string label, IDictionary<
string, IList<string>>? properties =
null )
398 System.Reflection.PropertyInfo[] typeProperties = recordClass.GetProperties( System.Reflection.BindingFlags.DeclaredOnly |
399 System.Reflection.BindingFlags.Instance |
400 System.Reflection.BindingFlags.Public );
402 Array.Sort( typeProperties, (p1, p2) => p1.MetadataToken.CompareTo( p2.MetadataToken ) );
405 List<Column> columns = [];
406 List<string> columnNames = [];
409 foreach ( var typeProperty
in typeProperties )
411 string columnName =
"";
413 IList<string>? columnProperties =
null;
414 bool isColumnNullable =
false;
417 columnName = typeProperty.Name;
419 Type? propertyType = typeProperty.PropertyType;
422 if ( typeProperty.PropertyType.IsGenericType &&
423 ( typeProperty.PropertyType.GetGenericTypeDefinition() == typeof( Nullable<> ) ) )
425 isColumnNullable =
true;
427 propertyType = Nullable.GetUnderlyingType(propertyType);
431 if ( propertyType == typeof( System.String ) )
435 else if ( propertyType == typeof( System.Int32 ) )
439 else if ( propertyType == typeof( System.Int64 ) )
443 else if ( propertyType == typeof(
float ) )
447 else if ( propertyType == typeof(
double ) )
451 else if ( propertyType == typeof(
byte ) )
457 ": " + propertyType +
458 " (must be one of int, long, float, double, string, and byte)" );
461 properties?.TryGetValue(columnName, out columnProperties);
464 columnNames.Add( columnName );
467 Column column =
new( columnName, columnType, columnProperties );
468 if ( isColumnNullable )
469 column.setIsNullable(
true );
472 columns.Add( column );
476 if (properties !=
null)
478 IEnumerable<string> propertyKeys = properties.Keys;
479 var unknownColumns = propertyKeys.Where(e => !columnNames.Contains(e));
481 if (unknownColumns.Any())
484 else properties =
new Dictionary<string, IList<string>>();
506 return fromObject( recordObj,
"", properties );
519 public static KineticaType fromObject(Object recordObj,
string label =
"", IDictionary<
string, IList<string>> properties =
null)
524 Type object_type = recordObj.GetType();
526 return fromClass( object_type, label, properties );
535 m_data.columns = columns;
545 public KineticaType(
string label, IList<Column> columns) : this(columns)
547 m_data.label = label;
556 public KineticaType(
string label, IList<Column> columns, IDictionary<
string, IList<string>> properties ) : this( label, columns )
558 m_properties = properties ??
new Dictionary<string, IList<string>>();
567 m_data.schemaString = typeSchema;
568 CreateSchemaFromString( typeSchema );
579 public KineticaType(
string label,
string typeSchema, IDictionary<
string, IList<string>> properties,
string? typeId =
null )
581 m_properties = properties;
583 m_data.label = label;
584 m_data.schemaString = typeSchema;
585 CreateSchemaFromString(typeSchema, properties);
595 public bool hasColumn(
string name) {
return m_data.columnMap.ContainsKey(name); }
607 this.m_data.sourceType = sourceType;
621 if ( this.m_data.sourceType !=
null )
622 kinetica.SetKineticaSourceClassToTypeMapping( this.m_data.sourceType,
this );
635 private void Initialize()
637 int columnCount = m_data.columns.Count;
639 if (columnCount == 0)
641 throw new ArgumentException(
"At least one column must be specified.");
644 for (
int i = 0; i < columnCount; ++i)
646 string columnName = m_data.columns[i].getName();
648 if (m_data.columnMap.ContainsKey(columnName))
650 throw new ArgumentException(
"Duplicate column name " + columnName +
" specified.");
653 m_data.columnMap[columnName] = i;
663 private void CreateSchemaFromString(
string typeSchema,
664 IDictionary<
string, IList<string>> properties =
null)
669 m_data.schema = RecordSchema.Parse(typeSchema);
673 throw new KineticaException(ex.ToString());
676 var root = JObject.Parse(typeSchema);
678 var rootType = root[
"type"];
679 if ((
null == rootType) || !rootType.ToString().Contains(
"record"))
681 throw new ArgumentException(
"Schema must be of type record.");
684 var fields = root[
"fields"];
685 if ((
null == fields) || !fields.HasValues)
687 throw new ArgumentException(
"Schema has no fields.");
690 foreach (var field
in fields)
698 string? fieldName = (
string?)field[
"name"];
699 if (
string.IsNullOrEmpty(fieldName))
701 throw new ArgumentException(
"Schema has unnamed field.");
704 if (m_data.columnMap.ContainsKey(fieldName))
706 throw new ArgumentException($
"Duplicate field name {fieldName}.");
709 var fieldType = field[
"type"];
710 if (
null == fieldType)
712 throw new ArgumentException($
"Field {fieldName} has no type.");
716 bool isColumnNullable =
false;
718 if (fieldType.HasValues)
720 var fieldTypeArray = fieldType;
722 foreach (var fieldTypeElement
in fieldTypeArray.Children())
727 var fieldTypeElementString = fieldTypeElement.ToString();
729 if (!
string.IsNullOrEmpty(fieldTypeElementString))
731 if (fieldTypeElementString ==
"null" || fieldTypeElementString ==
"\"null\"")
733 isColumnNullable =
true;
738 fieldType = fieldTypeElement;
746 throw new ArgumentException(
"Field {fieldName} has invalid type.");
751 Column.ColumnType columnType;
753 if (fieldType.ToString().Equals(
"bytes") || fieldType.ToString().Equals(
"\"bytes\""))
755 columnType = Column.ColumnType.BYTES;
757 else if (fieldType.ToString().Equals(
"double") || fieldType.ToString().Equals(
"\"double\""))
759 columnType = Column.ColumnType.DOUBLE;
761 else if (fieldType.ToString().Equals(
"float") || fieldType.ToString().Equals(
"\"float\""))
763 columnType = Column.ColumnType.FLOAT;
765 else if (fieldType.ToString().Equals(
"int") || fieldType.ToString().Equals(
"\"int\""))
767 columnType = Column.ColumnType.INT;
769 else if (fieldType.ToString().Equals(
"long") || fieldType.ToString().Equals(
"\"long\""))
771 columnType = Column.ColumnType.LONG;
773 else if (fieldType.ToString().Equals(
"string") || fieldType.ToString().Equals(
"\"string\""))
775 columnType = Column.ColumnType.STRING;
779 throw new ArgumentException(
"Field {fieldName} must be of type bytes, double, float, int, long or string.");
782 IList<string>? columnProperties =
null;
783 properties?.TryGetValue(fieldName, out columnProperties);
785 if ( (
null != columnProperties ) &&
786 ( columnProperties.Contains( ColumnProperty.NULLABLE ) ) )
787 isColumnNullable =
true;
790 Column column =
new( fieldName, columnType, columnProperties );
792 column.setIsNullable( isColumnNullable );
794 m_data.columns.Add( column );
796 m_data.columnMap[fieldName] = m_data.columns.Count - 1;
803 private void CreateSchema()
806 if (m_data.schema !=
null)
813 if (m_data.schemaString !=
null)
817 m_data.schema = RecordSchema.Parse(m_data.schemaString);
822 throw new KineticaException(ex.ToString());
830 string schemaString =
"";
833 string schemaOpening =
"{'type':'record','name':'type_name','fields':[";
835 string schemaClosing =
"]}";
837 schemaString += schemaOpening;
840 foreach (var column
in m_data.columns)
843 string fieldName = (
"'name':'" + column.getName() +
"'");
846 string fieldType =
"";
847 if (column.isNullable())
849 fieldType = (
"['" + column.getTypeString() +
"','null']");
853 fieldType = (
"'" + column.getTypeString() +
"'" );
855 fieldType = (
"'type':" + fieldType);
858 string field = (
"{" + fieldName +
"," + fieldType +
"},");
859 schemaString += field;
863 char[] comma = [
','];
864 schemaString = schemaString.TrimEnd(comma);
866 schemaString += schemaClosing;
871 m_data.schema = RecordSchema.Parse(schemaString);
875 throw new KineticaException(ex.ToString());
879 m_data.schemaString = m_data.schema.ToString();
const string CHAR1
This property provides optimized memory, disk and query performance for string columns.
ColumnType getType()
Returns the enumeration for the column type.
const string DATETIME
Valid only for 'string' columns.
const string INT16
This property provides optimized memory and query performance for int columns.
const string CHAR128
This property provides optimized memory, disk and query performance for string columns.
bool hasColumn(string name)
KineticaType(string typeSchema)
Create a KineticaType object using the string-formatted schema for the type.
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.
string type_id
An identifier representing the created type.
KineticaType(string label, IList< Column > columns)
Create a KineticaType object with the given column and label information.
int getColumnIndex(string name)
Column properties used for Kinetica types.
Column(string name, ColumnType type, IList< string >? properties=null)
Creates a Column object from the given name, type, and properties.
const string TIMESTAMP
Valid only for 'long' columns.
const string CHAR16
This property provides optimized memory, disk and query performance for string columns.
static KineticaType fromTable(Kinetica kinetica, string tableName)
Create a KineticaType object based on an existing table in the database.
static KineticaType fromTypeID(Kinetica kinetica, string typeId)
Create a KineticaType object based on an existing type in the database.
const string CHAR64
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.
Column getColumn(int index)
const string CHAR4
This property provides optimized memory, disk and query performance for string columns.
const string CHAR2
This property provides optimized memory, disk and query performance for string columns.
string getTypeString()
Returns the string format of the data type.
const string DATE
Valid only for 'string' columns.
const string CHAR8
This property provides optimized memory, disk and query performance for string columns.
const string DECIMAL
Valid only 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.
const string CHAR32
This property provides optimized memory, disk and query performance for string columns.
IList< string > getProperties()
Returns the properties for the column.
IList< Column > getColumns()
const string IPV4
This property provides optimized memory, disk and query performance for string columns representing I...
void saveSourceType(Type sourceType)
Saves the given type as this KineticaType's source type.
const string CHAR256
This property provides optimized memory, disk and query performance for string columns.
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.
bool isNullable()
Returns if the column is nullable.
Column getColumn(string name)
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.
const string INT8
This property provides optimized memory and query performance for int columns.
string create(Kinetica kinetica)
Given a handle to the server, creates a type in the database based on this data type.
string getName()
Returns the name of the column.
override string ToString()
KineticaType(IList< Column > columns)
Create a KineticaType object with the given column information.
const string TIME
Valid only for 'string' columns.
const string NULLABLE
This property indicates that this column is nullable.
A set of results returned by Kinetica.createType.
API to talk to Kinetica Database
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.
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.