mirror of
https://github.com/AlexMacocian/Squealify.git
synced 2026-07-15 14:59:32 +00:00
324 lines
15 KiB
C#
324 lines
15 KiB
C#
using Squealify.Contexts;
|
|
using Sybil;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace Squealify.Table;
|
|
public static class BasicQueryMethodGenerator
|
|
{
|
|
public static MethodWithSqlStatement CreateInsertStatement(TableContext context)
|
|
{
|
|
var placeHolder = $"Insert{context.DboType}";
|
|
var sql = new StringBuilder();
|
|
sql.AppendLine()
|
|
.AppendLine($"\t\t\tINSERT INTO {context.TableName}")
|
|
.AppendLine($"\t\t\t\t({string.Join(", ", context.Fields.Select(f => f.Name))})")
|
|
.AppendLine("\t\t\tVALUES")
|
|
.AppendLine($"\t\t\t\t({string.Join(", ", context.Fields.Select(GetPlaceholderName))})");
|
|
|
|
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.ValueTaskType, Constants.InsertMethodName)
|
|
.WithModifiers($"{Constants.Public} {Constants.Async}")
|
|
.WithParameter(context.DboType, Constants.DboArgumentName)
|
|
.WithParameter(Constants.CancellationTokenType, Constants.CancellationTokenArgument)
|
|
.WithBody(GenerateFullBody(placeHolder, context));
|
|
|
|
return new MethodWithSqlStatement(methodBuilder, placeHolder, sql.ToString());
|
|
}
|
|
|
|
public static MethodWithSqlStatement CreateUpsertStatement(TableContext context)
|
|
{
|
|
var placeHolder = $"Upsert{context.DboType}";
|
|
var sql = new StringBuilder();
|
|
sql.AppendLine()
|
|
.AppendLine($"\t\t\tINSERT INTO {context.TableName}")
|
|
.AppendLine($"\t\t\t\t({string.Join(", ", context.Fields.Select(f => f.Name))})")
|
|
.AppendLine("\t\t\tVALUES")
|
|
.AppendLine($"\t\t\t\t({string.Join(", ", context.Fields.Select(GetPlaceholderName))})")
|
|
.AppendLine($"\t\t\tON CONFLICT({context.PrimaryKey.Name}) DO UPDATE SET");
|
|
|
|
for(var i = 0; i < context.Fields.Length; i++)
|
|
{
|
|
var fieldContext = context.Fields[i];
|
|
if (fieldContext.Name == context.PrimaryKey.Name)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sql.Append($"\t\t\t\t{fieldContext.Name} = excluded.{fieldContext.Name}");
|
|
if (i < context.Fields.Length - 1)
|
|
{
|
|
sql.AppendLine(",");
|
|
}
|
|
}
|
|
|
|
sql.AppendLine(";");
|
|
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.ValueTaskType, Constants.UpsertMethodName)
|
|
.WithModifiers($"{Constants.Public} {Constants.Async}")
|
|
.WithParameter(context.DboType, Constants.DboArgumentName)
|
|
.WithParameter(Constants.CancellationTokenType, Constants.CancellationTokenArgument)
|
|
.WithBody(GenerateFullBody(placeHolder, context));
|
|
|
|
return new MethodWithSqlStatement(methodBuilder, placeHolder, sql.ToString());
|
|
}
|
|
|
|
public static MethodWithSqlStatement CreateUpdateStatement(TableContext context)
|
|
{
|
|
var placeHolder = $"Update{context.DboType}";
|
|
var sql = new StringBuilder();
|
|
sql.AppendLine()
|
|
.AppendLine($"\t\t\tUPDATE {context.TableName}")
|
|
.AppendLine("\t\t\tSET");
|
|
|
|
for (var i = 0; i < context.Fields.Length; i++)
|
|
{
|
|
var fieldContext = context.Fields[i];
|
|
if (fieldContext.Name == context.PrimaryKey.Name)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sql.Append($"\t\t\t\t{fieldContext.Name} = {GetPlaceholderName(fieldContext)}");
|
|
if (i < context.Fields.Length - 1)
|
|
{
|
|
sql.AppendLine(",");
|
|
}
|
|
}
|
|
|
|
sql.AppendLine()
|
|
.AppendLine("\t\t\tWHERE")
|
|
.AppendLine($"\t\t\t\t{context.PrimaryKey.Name} = {GetPlaceholderName(context.PrimaryKey)};");
|
|
|
|
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.ValueTaskType, Constants.UpdateMethodName)
|
|
.WithModifiers($"{Constants.Public} {Constants.Async}")
|
|
.WithParameter(context.DboType, Constants.DboArgumentName)
|
|
.WithParameter(Constants.CancellationTokenType, Constants.CancellationTokenArgument)
|
|
.WithBody(GenerateFullBody(placeHolder, context));
|
|
|
|
return new MethodWithSqlStatement(methodBuilder, placeHolder, sql.ToString());
|
|
}
|
|
|
|
public static MethodWithSqlStatement CreateDeleteStatement(TableContext context)
|
|
{
|
|
var placeHolder = $"Delete{context.DboType}";
|
|
var sql = new StringBuilder();
|
|
sql.AppendLine()
|
|
.AppendLine($"\t\t\tDELETE FROM {context.TableName}")
|
|
.AppendLine($"\t\t\tWHERE {context.PrimaryKey.Name} = {GetPlaceholderName(context.PrimaryKey)};");
|
|
|
|
var bodyBuilder = new StringBuilder(@$"
|
|
using var command = this.{Constants.DbConnectionPropertyName}.CreateCommand();
|
|
command.CommandText = @""{placeHolder}"";
|
|
");
|
|
|
|
if (!DataTypeMappings.DataTypeToString.TryGetValue(context.PrimaryKey.Type, out var convertedType))
|
|
{
|
|
throw new InvalidOperationException($"Unable to find conversion for {context.PrimaryKey.Type}");
|
|
}
|
|
|
|
(var convertToName, _) = SyntaxParsers.GetConversionNames(context.PrimaryKey.PropertyType, convertedType);
|
|
var valueProvider = context.PrimaryKey.RequiresConversion
|
|
? $"this.{convertToName}({Constants.PrimaryKeyArgumentName})"
|
|
: $"{Constants.PrimaryKeyArgumentName}";
|
|
|
|
bodyBuilder.AppendLine($"command.Parameters.Add(this.{Constants.CreateParameterMethod}(command, \"{GetPlaceholderName(context.PrimaryKey)}\", {valueProvider}));");
|
|
bodyBuilder.AppendLine("await command.ExecuteNonQueryAsync(cancellationToken);");
|
|
|
|
var methodBuilder = SyntaxBuilder.CreateMethod(Constants.ValueTaskType, Constants.DeleteMethodName)
|
|
.WithModifiers($"{Constants.Public} {Constants.Async}")
|
|
.WithParameter(context.PrimaryKey.PropertyType, Constants.PrimaryKeyArgumentName)
|
|
.WithParameter(Constants.CancellationTokenType, Constants.CancellationTokenArgument)
|
|
.WithBody(bodyBuilder.ToString());
|
|
|
|
return new MethodWithSqlStatement(methodBuilder, placeHolder, sql.ToString());
|
|
}
|
|
|
|
public static MethodWithSqlStatement CreateFindStatement(TableContext context)
|
|
{
|
|
var placeHolder = $"Find{context.DboType}";
|
|
var sql = new StringBuilder();
|
|
sql.AppendLine()
|
|
.AppendLine($"\t\t\tSELECT * FROM {context.TableName}")
|
|
.AppendLine($"\t\t\tWHERE {context.PrimaryKey.Name} = {GetPlaceholderName(context.PrimaryKey)};");
|
|
|
|
var bodyBuilder = new StringBuilder(@$"
|
|
using var command = this.{Constants.DbConnectionPropertyName}.CreateCommand();
|
|
command.CommandText = @""{placeHolder}"";
|
|
");
|
|
|
|
if (!DataTypeMappings.DataTypeToString.TryGetValue(context.PrimaryKey.Type, out var convertedType))
|
|
{
|
|
throw new InvalidOperationException($"Unable to find conversion for {context.PrimaryKey.Type}");
|
|
}
|
|
|
|
(var convertToName, _) = SyntaxParsers.GetConversionNames(context.PrimaryKey.PropertyType, convertedType);
|
|
var valueProvider = context.PrimaryKey.RequiresConversion
|
|
? $"this.{convertToName}({Constants.PrimaryKeyArgumentName})"
|
|
: $"{Constants.PrimaryKeyArgumentName}";
|
|
|
|
bodyBuilder.AppendLine($"command.Parameters.Add(this.{Constants.CreateParameterMethod}(command, \"{GetPlaceholderName(context.PrimaryKey)}\", {valueProvider}));");
|
|
bodyBuilder.AppendLine($"using var reader = await command.ExecuteReaderAsync({Constants.CancellationTokenArgument});");
|
|
bodyBuilder.AppendLine($"if (!await reader.ReadAsync({Constants.CancellationTokenArgument})) return default;");
|
|
bodyBuilder.AppendLine($"return new {context.DboType}");
|
|
bodyBuilder.AppendLine("{");
|
|
foreach((var index, var fieldContext) in context.Fields.Select((f, i) => (i, f)))
|
|
{
|
|
if (!DataTypeMappings.DataTypeToString.TryGetValue(fieldContext.Type, out var convertedFieldType))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
(_, var convertFromName) = SyntaxParsers.GetConversionNames(fieldContext.PropertyType, convertedFieldType);
|
|
|
|
var fieldValueProvider = fieldContext.RequiresConversion
|
|
? $"this.{convertFromName}({GetReaderMethod("reader", fieldContext)}({index}))"
|
|
: $"{GetReaderMethod("reader", fieldContext)}({index})";
|
|
|
|
var nullCheckGuardedProvider = fieldContext.IsNullable
|
|
? $"await reader.IsDBNullAsync({index}, {Constants.CancellationTokenArgument}) ? default : {fieldValueProvider}"
|
|
: fieldValueProvider;
|
|
|
|
bodyBuilder.Append($"{fieldContext.PropertyName} = {nullCheckGuardedProvider}");
|
|
if (index < context.Fields.Length - 1)
|
|
{
|
|
bodyBuilder.AppendLine(",");
|
|
}
|
|
}
|
|
bodyBuilder
|
|
.AppendLine()
|
|
.AppendLine("};");
|
|
|
|
var methodBuilder = SyntaxBuilder.CreateMethod($"{Constants.ValueTaskType}<{context.DboType}?>", Constants.FindMethodName)
|
|
.WithModifiers($"{Constants.Public} {Constants.Async}")
|
|
.WithParameter(context.PrimaryKey.PropertyType, Constants.PrimaryKeyArgumentName)
|
|
.WithParameter(Constants.CancellationTokenType, Constants.CancellationTokenArgument)
|
|
.WithBody(bodyBuilder.ToString());
|
|
|
|
return new MethodWithSqlStatement(methodBuilder, placeHolder, sql.ToString());
|
|
}
|
|
|
|
public static MethodWithSqlStatement CreateFindAllStatement(TableContext context)
|
|
{
|
|
var placeHolder = $"FindAll{context.DboType}";
|
|
var sql = new StringBuilder();
|
|
sql.AppendLine()
|
|
.AppendLine($"\t\t\tSELECT * FROM {context.TableName};");
|
|
|
|
var bodyBuilder = new StringBuilder(@$"
|
|
using var command = this.{Constants.DbConnectionPropertyName}.CreateCommand();
|
|
command.CommandText = @""{placeHolder}"";
|
|
");
|
|
|
|
if (!DataTypeMappings.DataTypeToString.TryGetValue(context.PrimaryKey.Type, out var convertedType))
|
|
{
|
|
throw new InvalidOperationException($"Unable to find conversion for {context.PrimaryKey.Type}");
|
|
}
|
|
|
|
bodyBuilder.AppendLine($"using var reader = await command.ExecuteReaderAsync({Constants.CancellationTokenArgument});");
|
|
bodyBuilder.AppendLine($"while (await reader.ReadAsync({Constants.CancellationTokenArgument}))");
|
|
bodyBuilder.AppendLine("{");
|
|
bodyBuilder.AppendLine($"yield return new {context.DboType}");
|
|
bodyBuilder.AppendLine("{");
|
|
foreach ((var index, var fieldContext) in context.Fields.Select((f, i) => (i, f)))
|
|
{
|
|
if (!DataTypeMappings.DataTypeToString.TryGetValue(fieldContext.Type, out var convertedFieldType))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
(_, var convertFromName) = SyntaxParsers.GetConversionNames(fieldContext.PropertyType, convertedFieldType);
|
|
|
|
var fieldValueProvider = fieldContext.RequiresConversion
|
|
? $"this.{convertFromName}({GetReaderMethod("reader", fieldContext)}({index}))"
|
|
: $"{GetReaderMethod("reader", fieldContext)}({index})";
|
|
|
|
var nullCheckGuardedProvider = fieldContext.IsNullable
|
|
? $"await reader.IsDBNullAsync({index}, {Constants.CancellationTokenArgument}) ? default : {fieldValueProvider}"
|
|
: fieldValueProvider;
|
|
|
|
bodyBuilder.Append($"{fieldContext.PropertyName} = {nullCheckGuardedProvider}");
|
|
if (index < context.Fields.Length - 1)
|
|
{
|
|
bodyBuilder.AppendLine(",");
|
|
}
|
|
}
|
|
bodyBuilder
|
|
.AppendLine()
|
|
.AppendLine("};")
|
|
.AppendLine("}");
|
|
|
|
var methodBuilder = SyntaxBuilder.CreateMethod($"{Constants.IAsyncEnumerableType}<{context.DboType}>", Constants.FindAllMethodName)
|
|
.WithModifiers($"{Constants.Public} {Constants.Async}")
|
|
.WithParameter(context.PrimaryKey.PropertyType, Constants.PrimaryKeyArgumentName)
|
|
.WithParameter($"[{Constants.EnumeratorCancellationAttribute}] {Constants.CancellationTokenType}", Constants.CancellationTokenArgument)
|
|
.WithBody(bodyBuilder.ToString());
|
|
|
|
return new MethodWithSqlStatement(methodBuilder, placeHolder, sql.ToString());
|
|
}
|
|
|
|
private static string GenerateFullBody(string placeHolder, TableContext context)
|
|
{
|
|
var bodyBuilder = new StringBuilder(@$"
|
|
using var command = this.{Constants.DbConnectionPropertyName}.CreateCommand();
|
|
command.CommandText = @""{placeHolder}"";
|
|
");
|
|
|
|
foreach (var fieldContext in context.Fields)
|
|
{
|
|
var propertyType = fieldContext.PropertyType;
|
|
var dataType = fieldContext.Type;
|
|
if (!DataTypeMappings.DataTypeToString.TryGetValue(dataType, out var convertedType))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
(var convertToName, _) = SyntaxParsers.GetConversionNames(propertyType, convertedType);
|
|
var valueProvider = fieldContext.RequiresConversion
|
|
? $"this.{convertToName}({Constants.DboArgumentName}.{fieldContext.PropertyName})"
|
|
: $"{Constants.DboArgumentName}.{fieldContext.PropertyName}";
|
|
|
|
bodyBuilder.AppendLine($"command.Parameters.Add(this.{Constants.CreateParameterMethod}(command, \"{GetPlaceholderName(fieldContext)}\", {valueProvider}));");
|
|
}
|
|
|
|
bodyBuilder.AppendLine("await command.ExecuteNonQueryAsync(cancellationToken);");
|
|
return bodyBuilder.ToString();
|
|
}
|
|
|
|
private static string GetPlaceholderName(FieldContext context) => $"@{context.Name}";
|
|
|
|
private static string GetReaderMethod(string readerVariableName, FieldContext context)
|
|
{
|
|
var readMethod = context.Type switch
|
|
{
|
|
DataTypes.SMALLINT => "GetInt16",
|
|
DataTypes.INTEGER => "GetInt32",
|
|
DataTypes.BIGINT => "GetInt64",
|
|
DataTypes.REAL => "GetFloat",
|
|
DataTypes.DOUBLE_PRECISION => "GetDouble",
|
|
DataTypes.DECIMAL => "GetDecimal",
|
|
DataTypes.CHAR => "GetChar",
|
|
DataTypes.VARCHAR => "GetString",
|
|
DataTypes.TEXT => "GetString",
|
|
DataTypes.DATE => "GetDateTime",
|
|
DataTypes.TIME => "GetDateTime",
|
|
DataTypes.TIMESTAMP => "GetDateTime",
|
|
DataTypes.BOOLEAN => "GetBoolean",
|
|
DataTypes.BLOB => "GetBytes",
|
|
_ => throw new NotSupportedException($"Data type {context.Type} is not supported.")
|
|
};
|
|
|
|
var readerMethod = context.IsEnum
|
|
? $"({context.PropertyType}){readerVariableName}.{readMethod}"
|
|
: $"{readerVariableName}.{readMethod}";
|
|
|
|
if(context.PropertyType.ToLower().TrimEnd('?') is "datetimeoffset" or "system.datetimeoffset")
|
|
{
|
|
return context.IsNullable
|
|
? $"(DateTimeOffset?){readerMethod}"
|
|
: $"(DateTimeOffset){readerMethod}";
|
|
}
|
|
|
|
return readerMethod;
|
|
}
|
|
}
|