|
|
|
|
using Connected.Expressions.Languages;
|
|
|
|
|
using Connected.Expressions.TypeSystem;
|
|
|
|
|
using Connected.Interop;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace Connected.ServiceModel.Client.Data;
|
|
|
|
|
internal sealed class CqlTypeSystem : QueryTypeSystem
|
|
|
|
|
{
|
|
|
|
|
public static int StringDefaultSize => int.MaxValue;
|
|
|
|
|
public static int BinaryDefaultSize => int.MaxValue;
|
|
|
|
|
|
|
|
|
|
public override DataType Parse(string typeDeclaration)
|
|
|
|
|
{
|
|
|
|
|
string[]? args = null;
|
|
|
|
|
string typeName;
|
|
|
|
|
string? remainder = null;
|
|
|
|
|
var openParen = typeDeclaration.IndexOf('(');
|
|
|
|
|
|
|
|
|
|
if (openParen >= 0)
|
|
|
|
|
{
|
|
|
|
|
typeName = typeDeclaration[..openParen].Trim();
|
|
|
|
|
|
|
|
|
|
var closeParen = typeDeclaration.IndexOf(')', openParen);
|
|
|
|
|
|
|
|
|
|
if (closeParen < openParen)
|
|
|
|
|
closeParen = typeDeclaration.Length;
|
|
|
|
|
|
|
|
|
|
var argstr = typeDeclaration[(openParen + 1)..closeParen];
|
|
|
|
|
|
|
|
|
|
args = argstr.Split(',');
|
|
|
|
|
|
|
|
|
|
remainder = typeDeclaration[(closeParen + 1)..];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var space = typeDeclaration.IndexOf(' ');
|
|
|
|
|
|
|
|
|
|
if (space >= 0)
|
|
|
|
|
{
|
|
|
|
|
typeName = typeDeclaration[..space];
|
|
|
|
|
remainder = typeDeclaration[(space + 1)..].Trim();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
typeName = typeDeclaration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var isNotNull = (remainder is not null) && remainder.ToUpper().Contains("NOT NULL");
|
|
|
|
|
|
|
|
|
|
return ResolveDataType(typeName, args, isNotNull);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public DataType ResolveDataType(string typeName, string[] args, bool isNotNull)
|
|
|
|
|
{
|
|
|
|
|
if (string.Equals(typeName, "rowversion", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
typeName = "Timestamp";
|
|
|
|
|
|
|
|
|
|
if (string.Equals(typeName, "numeric", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
typeName = "Decimal";
|
|
|
|
|
|
|
|
|
|
if (string.Equals(typeName, "sql_variant", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
typeName = "Variant";
|
|
|
|
|
|
|
|
|
|
var dbType = ResolveSqlType(typeName);
|
|
|
|
|
var length = 0;
|
|
|
|
|
short precision = 0;
|
|
|
|
|
short scale = 0;
|
|
|
|
|
|
|
|
|
|
switch (dbType)
|
|
|
|
|
{
|
|
|
|
|
case SqlDbType.Binary:
|
|
|
|
|
case SqlDbType.Char:
|
|
|
|
|
case SqlDbType.Image:
|
|
|
|
|
case SqlDbType.NChar:
|
|
|
|
|
case SqlDbType.NVarChar:
|
|
|
|
|
case SqlDbType.VarBinary:
|
|
|
|
|
case SqlDbType.VarChar:
|
|
|
|
|
length = args is null || !args.Any() ? 32 : string.Equals(args[0], "max", StringComparison.OrdinalIgnoreCase) ? int.MaxValue : int.Parse(args[0]);
|
|
|
|
|
break;
|
|
|
|
|
case SqlDbType.Money:
|
|
|
|
|
precision = args is null || !args.Any() ? (short)29 : short.Parse(args[0], NumberFormatInfo.InvariantInfo);
|
|
|
|
|
scale = args is null || args.Length < 2 ? (short)4 : short.Parse(args[1], NumberFormatInfo.InvariantInfo);
|
|
|
|
|
break;
|
|
|
|
|
case SqlDbType.Decimal:
|
|
|
|
|
precision = args is null || !args.Any() ? (short)29 : short.Parse(args[0], NumberFormatInfo.InvariantInfo);
|
|
|
|
|
scale = args is null || args.Length < 2 ? (short)0 : short.Parse(args[1], NumberFormatInfo.InvariantInfo);
|
|
|
|
|
break;
|
|
|
|
|
case SqlDbType.Float:
|
|
|
|
|
case SqlDbType.Real:
|
|
|
|
|
precision = args is null || !args.Any() ? (short)29 : short.Parse(args[0], NumberFormatInfo.InvariantInfo);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NewType(dbType, isNotNull, length, precision, scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static DataType NewType(SqlDbType type, bool isNotNull, int length, short precision, short scale)
|
|
|
|
|
{
|
|
|
|
|
return new CqlDataType(type, isNotNull, length, precision, scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static SqlDbType ResolveSqlType(string typeName)
|
|
|
|
|
{
|
|
|
|
|
return (SqlDbType)Enum.Parse(typeof(SqlDbType), typeName, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override DataType ResolveColumnType(Type type)
|
|
|
|
|
{
|
|
|
|
|
var isNotNull = type.GetTypeInfo().IsValueType && !Nullables.IsNullableType(type);
|
|
|
|
|
type = Nullables.GetNonNullableType(type);
|
|
|
|
|
|
|
|
|
|
switch (Interop.TypeSystem.GetTypeCode(type))
|
|
|
|
|
{
|
|
|
|
|
case TypeCode.Boolean:
|
|
|
|
|
return NewType(SqlDbType.Bit, isNotNull, 0, 0, 0);
|
|
|
|
|
case TypeCode.SByte:
|
|
|
|
|
case TypeCode.Byte:
|
|
|
|
|
return NewType(SqlDbType.TinyInt, isNotNull, 0, 0, 0);
|
|
|
|
|
case TypeCode.Int16:
|
|
|
|
|
case TypeCode.UInt16:
|
|
|
|
|
return NewType(SqlDbType.SmallInt, isNotNull, 0, 0, 0);
|
|
|
|
|
case TypeCode.Int32:
|
|
|
|
|
case TypeCode.UInt32:
|
|
|
|
|
return NewType(SqlDbType.Int, isNotNull, 0, 0, 0);
|
|
|
|
|
case TypeCode.Int64:
|
|
|
|
|
case TypeCode.UInt64:
|
|
|
|
|
return NewType(SqlDbType.BigInt, isNotNull, 0, 0, 0);
|
|
|
|
|
case TypeCode.Single:
|
|
|
|
|
case TypeCode.Double:
|
|
|
|
|
return NewType(SqlDbType.Float, isNotNull, 0, 0, 0);
|
|
|
|
|
case TypeCode.String:
|
|
|
|
|
return NewType(SqlDbType.NVarChar, isNotNull, StringDefaultSize, 0, 0);
|
|
|
|
|
case TypeCode.Char:
|
|
|
|
|
return NewType(SqlDbType.NChar, isNotNull, 1, 0, 0);
|
|
|
|
|
case TypeCode.DateTime:
|
|
|
|
|
return NewType(SqlDbType.DateTime, isNotNull, 0, 0, 0);
|
|
|
|
|
case TypeCode.Decimal:
|
|
|
|
|
return NewType(SqlDbType.Decimal, isNotNull, 0, 29, 4);
|
|
|
|
|
default:
|
|
|
|
|
if (type == typeof(byte[]))
|
|
|
|
|
return NewType(SqlDbType.VarBinary, isNotNull, BinaryDefaultSize, 0, 0);
|
|
|
|
|
else if (type == typeof(Guid))
|
|
|
|
|
return NewType(SqlDbType.UniqueIdentifier, isNotNull, 0, 0, 0);
|
|
|
|
|
else if (type == typeof(DateTimeOffset))
|
|
|
|
|
return NewType(SqlDbType.DateTimeOffset, isNotNull, 0, 0, 0);
|
|
|
|
|
else if (type == typeof(TimeSpan))
|
|
|
|
|
return NewType(SqlDbType.Time, isNotNull, 0, 0, 0);
|
|
|
|
|
else if (type.GetTypeInfo().IsEnum)
|
|
|
|
|
return NewType(SqlDbType.Int, isNotNull, 0, 0, 0);
|
|
|
|
|
else
|
|
|
|
|
throw new NotSupportedException(nameof(ResolveColumnType));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsVariableLength(SqlDbType dbType)
|
|
|
|
|
{
|
|
|
|
|
return dbType switch
|
|
|
|
|
{
|
|
|
|
|
SqlDbType.Image or SqlDbType.NText or SqlDbType.NVarChar or SqlDbType.Text or SqlDbType.VarBinary or SqlDbType.VarChar or SqlDbType.Xml => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string Format(DataType type, bool suppressSize)
|
|
|
|
|
{
|
|
|
|
|
var sqlType = (CqlDataType)type;
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.Append(sqlType.DbType.ToString().ToUpper());
|
|
|
|
|
|
|
|
|
|
if (sqlType.Length > 0 && !suppressSize)
|
|
|
|
|
{
|
|
|
|
|
if (sqlType.Length == int.MaxValue)
|
|
|
|
|
sb.Append("(max)");
|
|
|
|
|
else
|
|
|
|
|
sb.AppendFormat(NumberFormatInfo.InvariantInfo, "({0})", sqlType.Length);
|
|
|
|
|
}
|
|
|
|
|
else if (sqlType.Precision != 0)
|
|
|
|
|
{
|
|
|
|
|
if (sqlType.Scale != 0)
|
|
|
|
|
sb.AppendFormat(NumberFormatInfo.InvariantInfo, "({0},{1})", sqlType.Precision, sqlType.Scale);
|
|
|
|
|
else
|
|
|
|
|
sb.AppendFormat(NumberFormatInfo.InvariantInfo, "({0})", sqlType.Precision);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|