You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
5.9 KiB

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();
}
}