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.
Connected.Framework/Connected.Expressions/Serialization/DatabaseSerializer.cs

245 lines
5.7 KiB

2 years ago
using System.Linq.Expressions;
using Connected.Expressions.Evaluation;
using Connected.Expressions.Expressions;
using Connected.Expressions.Expressions.Serialization;
using Connected.Expressions.Formatters;
using Connected.Expressions.Languages;
using Connected.Expressions.Translation;
namespace Connected.Expressions.Serialization;
internal sealed class DatabaseSerializer : ExpressionSerializer
{
public DatabaseSerializer(TextWriter writer, QueryLanguage? language) : base(writer)
{
Aliases = new();
Language = language;
}
private Dictionary<Alias, int> Aliases { get; }
private QueryLanguage? Language { get; }
public static new void Serialize(TextWriter writer, Expression expression)
{
Serialize(writer, expression, null);
}
public static void Serialize(TextWriter writer, Expression expression, QueryLanguage? language)
{
new DatabaseSerializer(writer, language).Visit(expression);
}
public new static string Serialize(Expression expression)
{
return Serialize((QueryLanguage?)null, expression);
}
public static string Serialize(QueryLanguage? language, Expression expression)
{
var writer = new StringWriter();
Serialize(writer, expression, language);
return writer.ToString();
}
protected override Expression? Visit(Expression? expression)
{
if (expression is null)
return default;
switch ((DatabaseExpressionType)expression.NodeType)
{
case DatabaseExpressionType.Projection:
return VisitProjection((ProjectionExpression)expression);
case DatabaseExpressionType.ClientJoin:
return VisitClientJoin((ClientJoinExpression)expression);
case DatabaseExpressionType.Select:
return VisitSelect((SelectExpression)expression);
case DatabaseExpressionType.OuterJoined:
return VisitOuterJoined((OuterJoinedExpression)expression);
case DatabaseExpressionType.Column:
return VisitColumn((ColumnExpression)expression);
case DatabaseExpressionType.If:
case DatabaseExpressionType.Block:
case DatabaseExpressionType.Declaration:
return VisitCommand((CommandExpression)expression);
case DatabaseExpressionType.Batch:
return VisitBatch((BatchExpression)expression);
case DatabaseExpressionType.Function:
return VisitFunction((FunctionExpression)expression);
case DatabaseExpressionType.Entity:
return VisitEntity((EntityExpression)expression);
default:
if (expression is DatabaseExpression)
{
Write(FormatQuery(expression));
return expression;
}
else
return base.Visit(expression);
}
}
private void AddAlias(Alias alias)
{
if (!Aliases.ContainsKey(alias))
Aliases.Add(alias, Aliases.Count);
}
private Expression VisitProjection(ProjectionExpression projection)
{
AddAlias(projection.Select.Alias);
Write("Project(");
WriteLine(Indentation.Inner);
Write("@\"");
Visit(projection.Select);
Write("\",");
WriteLine(Indentation.Same);
Visit(projection.Projector);
Write(',');
WriteLine(Indentation.Same);
Visit(projection.Aggregator);
WriteLine(Indentation.Outer);
Write(')');
return projection;
}
private Expression VisitClientJoin(ClientJoinExpression join)
{
AddAlias(join.Projection.Select.Alias);
Write("ClientJoin(");
WriteLine(Indentation.Inner);
Write("OuterKey(");
VisitExpressionList(join.OuterKey);
Write("),");
WriteLine(Indentation.Same);
Write("InnerKey(");
VisitExpressionList(join.InnerKey);
Write("),");
WriteLine(Indentation.Same);
Visit(join.Projection);
WriteLine(Indentation.Outer);
Write(')');
return join;
}
private Expression VisitOuterJoined(OuterJoinedExpression outer)
{
Write("Outer(");
WriteLine(Indentation.Inner);
Visit(outer.Test);
Write(", ");
WriteLine(Indentation.Same);
Visit(outer.Expression);
WriteLine(Indentation.Outer);
Write(')');
return outer;
}
private Expression VisitSelect(SelectExpression select)
{
Write(select.QueryText);
return select;
}
private Expression VisitCommand(CommandExpression command)
{
Write(FormatQuery(command));
return command;
}
private string FormatQuery(Expression query)
{
return SqlFormatter.Format(query);
}
private Expression VisitBatch(BatchExpression batch)
{
Write("Batch(");
WriteLine(Indentation.Inner);
Visit(batch.Input);
Write(",");
WriteLine(Indentation.Same);
Visit(batch.Operation);
Write(",");
WriteLine(Indentation.Same);
Visit(batch.BatchSize);
Write(", ");
Visit(batch.Stream);
WriteLine(Indentation.Outer);
Write(")");
return batch;
}
private Expression VisitVariable(VariableExpression vex)
{
Write(FormatQuery(vex));
return vex;
}
private Expression VisitFunction(FunctionExpression function)
{
Write("FUNCTION ");
Write(function.Name);
if (function.Arguments.Count > 0)
{
Write("(");
VisitExpressionList(function.Arguments);
Write(")");
}
return function;
}
private Expression VisitEntity(EntityExpression expression)
{
Visit(expression.Expression);
return expression;
}
protected override Expression VisitConstant(ConstantExpression c)
{
if (c.Type == typeof(Command))
{
var qc = (Command)c.Value;
Write("new Connected.Expressions.Evaluation.QueryCommand {");
WriteLine(Indentation.Inner);
Write("\"" + qc.CommandText + "\"");
Write(",");
WriteLine(Indentation.Same);
Visit(Expression.Constant(qc.Parameters));
Write(")");
WriteLine(Indentation.Outer);
return c;
}
return base.VisitConstant(c);
}
private Expression VisitColumn(ColumnExpression column)
{
var aliasName = Aliases.TryGetValue(column.Alias, out int iAlias) ? "A" + iAlias : "A" + (column.Alias is not null ? column.Alias.GetHashCode().ToString() : "") + "?";
Write(aliasName);
Write(".");
Write("Column(\"");
Write(column.Name);
Write("\")");
return column;
}
}