2 using System.Collections.Generic;
4 using System.Text.RegularExpressions;
6 using Newtonsoft.Json.Linq;
44 private static readonly Regex DecimalPattern =
new Regex(
45 @"decimal\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)",
46 RegexOptions.IgnoreCase | RegexOptions.Compiled);
48 private string m_name;
50 private bool m_isNullable;
51 private IList<string> m_properties;
52 private int m_precision;
54 private bool m_isDecimal;
67 m_properties = properties ?? [];
76 public string getName() {
return m_name; }
123 internal void setIsNullable(
bool val ) { m_isNullable = val; }
143 private void Initialize()
145 if (
string.IsNullOrEmpty(m_name))
147 throw new ArgumentException(
"Name must not be empty.");
152 case ColumnType.BYTES:
153 case ColumnType.DOUBLE:
154 case ColumnType.FLOAT:
156 case ColumnType.LONG:
157 case ColumnType.STRING:
158 case ColumnType.BOOLEAN:
162 throw new ArgumentException($
"Column {m_name} must be of type BYTES, DOUBLE, FLOAT, INT, LONG, STRING or BOOLEAN.");
170 foreach (var it
in m_properties)
172 if (
string.IsNullOrEmpty(it))
174 throw new ArgumentException(
"Properties must not be empty.");
195 private void SetDecimalInfo(
string property)
206 var match = DecimalPattern.Match(property);
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))
219 return $
"{m_name} ({m_type})";
223 private class TypeData
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;
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;
245 var response =
kinetica.showTable(tableName);
246 var typeIdCount = response.type_ids.Count;
248 if (typeIdCount == 0)
253 string typeId = response.type_ids[0];
256 for (
int i = 1; i < typeIdCount; ++i)
258 if (response.type_ids[i] != typeId)
265 return new KineticaType(response.type_labels[0], response.type_schemas[0], response.properties[0], typeId );
276 var response =
kinetica.showTypes(typeId,
"");
278 if (response.type_ids.Count < 1)
283 return new KineticaType(response.labels[0], response.type_schemas[0], response.properties[0]);
295 Object[] columnHeaders, Object[] columnTypes )
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." );
302 JObject dynamicSchemaJson;
305 dynamicSchemaJson = JObject.Parse( dynamicTableSchemaString );
307 catch ( Exception ex )
315 var isColumnNullable =
new Func<string, JObject, bool>( (columnName, schemaJson) => {
317 bool foundField =
false;
318 JToken? fieldList = schemaJson[
"fields"];
319 if (fieldList !=
null)
321 foreach (var field
in fieldList)
323 if ((
string?)field[
"name"] == columnName)
329 JToken? fieldType = field[
"type"];
330 if (fieldType !=
null)
332 var typeElement = fieldType[
"items"];
333 if (typeElement ==
null || typeElement is JValue)
336 if ((typeElement is JArray) && ((
string)(typeElement as JArray)[1] ==
"null"))
350 List<Column> columns = [];
351 Dictionary<string, IList<string>> columnProperties = [];
352 for (
int i = 0; i < columnHeaders.Length; ++i )
355 string column_name = (string)columnHeaders[i];
359 string columnTypeString = (string)columnTypes[i];
363 List<string> columnProperty = [];
369 switch ( columnTypeString )
396 columnProperty.Add( columnTypeString );
409 columnProperty.Add( columnTypeString );
420 columnProperty.Add( columnTypeString );
443 columnProperty.Add(columnTypeString);
446 else if (columnTypeString.StartsWith(
ColumnProperty.
VECTOR, StringComparison.OrdinalIgnoreCase))
449 columnProperty.Add(columnTypeString);
455 columnProperty.Add(columnTypeString);
465 if ( isColumnNullable( $
"column_{i + 1}", dynamicSchemaJson ) )
470 Column column =
new( column_name, columnType, columnProperty );
471 columns.Add( column );
474 columnProperties.Add( column_name, columnProperty );
479 return new KineticaType(
"", columns, columnProperties );
501 return fromClass( recordClass,
"", properties );
518 System.Reflection.PropertyInfo[] typeProperties = recordClass.GetProperties( System.Reflection.BindingFlags.DeclaredOnly |
519 System.Reflection.BindingFlags.Instance |
520 System.Reflection.BindingFlags.Public );
522 Array.Sort( typeProperties, (p1, p2) => p1.MetadataToken.CompareTo( p2.MetadataToken ) );
525 List<Column> columns = [];
526 List<string> columnNames = [];
529 foreach ( var typeProperty
in typeProperties )
531 string columnName =
"";
533 IList<string>? columnProperties =
null;
534 bool isColumnNullable =
false;
537 columnName = typeProperty.Name;
539 Type? propertyType = typeProperty.PropertyType;
542 if ( typeProperty.PropertyType.IsGenericType &&
543 ( typeProperty.PropertyType.GetGenericTypeDefinition() == typeof( Nullable<> ) ) )
545 isColumnNullable =
true;
547 propertyType = Nullable.GetUnderlyingType(propertyType);
551 if ( propertyType == typeof( System.String ) )
555 else if ( propertyType == typeof( System.Int32 ) )
559 else if ( propertyType == typeof( System.Int64 ) )
563 else if ( propertyType == typeof(
float ) )
567 else if ( propertyType == typeof(
double ) )
571 else if ( propertyType == typeof(
byte ) )
577 ": " + propertyType +
578 " (must be one of int, long, float, double, string, and byte)" );
581 properties?.TryGetValue(columnName, out columnProperties);
584 columnNames.Add( columnName );
587 Column column =
new( columnName, columnType, columnProperties );
588 if ( isColumnNullable )
589 column.setIsNullable(
true );
592 columns.Add( column );
596 if (properties !=
null)
598 IEnumerable<string> propertyKeys = properties.Keys;
599 var unknownColumns = propertyKeys.Where(e => !columnNames.Contains(e));
601 if (unknownColumns.Any())
604 else properties =
new Dictionary<string, IList<string>>();
626 return fromObject( recordObj,
"", properties );
639 public static KineticaType fromObject(Object recordObj,
string label =
"", IDictionary<
string, IList<string>> properties =
null)
644 Type object_type = recordObj.GetType();
646 return fromClass( object_type, label, properties );
655 m_data.columns = columns;
665 public KineticaType(
string label, IList<Column> columns) : this(columns)
667 m_data.label = label;
676 public KineticaType(
string label, IList<Column> columns, IDictionary<
string, IList<string>> properties ) : this( label, columns )
678 m_properties = properties ??
new Dictionary<string, IList<string>>();
687 m_data.schemaString = typeSchema;
688 CreateSchemaFromString( typeSchema );
699 public KineticaType(
string label,
string typeSchema, IDictionary<
string, IList<string>> properties,
string? typeId =
null )
701 m_properties = properties;
703 m_data.label = label;
704 m_data.schemaString = typeSchema;
705 CreateSchemaFromString(typeSchema, properties);
715 public bool hasColumn(
string name) {
return m_data.columnMap.ContainsKey(name); }
727 this.m_data.sourceType = sourceType;
741 if ( this.m_data.sourceType !=
null )
742 kinetica.SetKineticaSourceClassToTypeMapping( this.m_data.sourceType,
this );
755 private void Initialize()
757 int columnCount = m_data.columns.Count;
759 if (columnCount == 0)
761 throw new ArgumentException(
"At least one column must be specified.");
764 for (
int i = 0; i < columnCount; ++i)
766 string columnName = m_data.columns[i].getName();
768 if (m_data.columnMap.ContainsKey(columnName))
770 throw new ArgumentException(
"Duplicate column name " + columnName +
" specified.");
773 m_data.columnMap[columnName] = i;
783 private void CreateSchemaFromString(
string typeSchema,
784 IDictionary<
string, IList<string>> properties =
null)
796 var root = JObject.Parse(typeSchema);
798 var rootType = root[
"type"];
799 if ((
null == rootType) || !rootType.ToString().Contains(
"record"))
801 throw new ArgumentException(
"Schema must be of type record.");
804 var fields = root[
"fields"];
805 if ((
null == fields) || !fields.HasValues)
807 throw new ArgumentException(
"Schema has no fields.");
810 foreach (var field
in fields)
818 string? fieldName = (
string?)field[
"name"];
819 if (
string.IsNullOrEmpty(fieldName))
821 throw new ArgumentException(
"Schema has unnamed field.");
824 if (m_data.columnMap.ContainsKey(fieldName))
826 throw new ArgumentException($
"Duplicate field name {fieldName}.");
829 var fieldType = field[
"type"];
830 if (
null == fieldType)
832 throw new ArgumentException($
"Field {fieldName} has no type.");
836 bool isColumnNullable =
false;
838 if (fieldType.HasValues)
840 var fieldTypeArray = fieldType;
842 foreach (var fieldTypeElement
in fieldTypeArray.Children())
847 var fieldTypeElementString = fieldTypeElement.ToString();
849 if (!
string.IsNullOrEmpty(fieldTypeElementString))
851 if (fieldTypeElementString ==
"null" || fieldTypeElementString ==
"\"null\"")
853 isColumnNullable =
true;
858 fieldType = fieldTypeElement;
866 throw new ArgumentException(
"Field {fieldName} has invalid type.");
873 if (fieldType.ToString().Equals(
"bytes") || fieldType.ToString().Equals(
"\"bytes\""))
877 else if (fieldType.ToString().Equals(
"double") || fieldType.ToString().Equals(
"\"double\""))
881 else if (fieldType.ToString().Equals(
"float") || fieldType.ToString().Equals(
"\"float\""))
885 else if (fieldType.ToString().Equals(
"int") || fieldType.ToString().Equals(
"\"int\""))
889 else if (fieldType.ToString().Equals(
"long") || fieldType.ToString().Equals(
"\"long\""))
893 else if (fieldType.ToString().Equals(
"string") || fieldType.ToString().Equals(
"\"string\""))
897 else if (fieldType.ToString().Equals(
"boolean") || fieldType.ToString().Equals(
"\"boolean\""))
903 throw new ArgumentException(
"Field {fieldName} must be of type bytes, double, float, int, long, string or boolean.");
906 IList<string>? columnProperties =
null;
907 properties?.TryGetValue(fieldName, out columnProperties);
909 if ( (
null != columnProperties ) &&
911 isColumnNullable =
true;
914 Column column =
new( fieldName, columnType, columnProperties );
916 column.setIsNullable( isColumnNullable );
918 m_data.columns.Add( column );
920 m_data.columnMap[fieldName] = m_data.columns.Count - 1;
927 private void CreateSchema()
930 if (m_data.schema !=
null)
937 if (m_data.schemaString !=
null)
954 string schemaString =
"";
957 string schemaOpening =
"{'type':'record','name':'type_name','fields':[";
959 string schemaClosing =
"]}";
961 schemaString += schemaOpening;
964 foreach (var column
in m_data.columns)
967 string fieldName = (
"'name':'" + column.getName() +
"'");
970 string fieldType =
"";
971 if (column.isNullable())
973 fieldType = (
"['" + column.getTypeString() +
"','null']");
977 fieldType = (
"'" + column.getTypeString() +
"'" );
979 fieldType = (
"'type':" + fieldType);
982 string field = (
"{" + fieldName +
"," + fieldType +
"},");
983 schemaString += field;
987 char[] comma = [
','];
988 schemaString = schemaString.TrimEnd(comma);
990 schemaString += schemaClosing;
1003 m_data.schemaString = m_data.schema.ToString();
const string CHAR64
This property provides optimized memory, disk and query performance for string columns.
string type_id
An identifier representing the created type.
const string INT16
This property provides optimized memory and query performance for int columns.
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.
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
KineticaData - class to help with Avro Encoding for Kinetica
const int DEFAULT_DECIMAL_SCALE
Default scale for decimal columns (matches Java API).
const string WKT
Valid only for 'string' and 'bytes' columns.
bool isNullable()
Returns if the column is nullable.
const int DEFAULT_DECIMAL_PRECISION
Default precision for decimal columns (matches Java API).
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
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.
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.
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.
Column getColumn(int index)
string getName()
Returns the name of the column.
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.
bool hasColumn(string name)
const int DECIMAL8_MAX_PRECISION
Maximum precision for 8-byte decimals.
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.
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.
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.
Immutable collection of metadata about a Kinetica type.
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
A set of results returned by Kinetica.createType.