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