Skip to content

Commit

Permalink
Added PRINT support
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkMpn committed Feb 16, 2022
1 parent 75cc961 commit 5577a11
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 0 deletions.
18 changes: 18 additions & 0 deletions MarkMpn.Sql4Cds.Engine.Tests/AdoProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,5 +324,23 @@ SELECT @param1
CollectionAssert.AreEqual(new[] { "a", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, results);
}
}

[TestMethod]
public void Print()
{
using (var con = new Sql4CdsConnection(_localDataSource.Values.ToList(), this))
using (var cmd = con.CreateCommand())
{
cmd.CommandText = "PRINT @param1";
cmd.Parameters.Add(new Sql4CdsParameter("@param1", 1));

var log = "";
con.InfoMessage += (s, e) => log += e.Message;

cmd.ExecuteNonQuery();

Assert.AreEqual("1", log);
}
}
}
}
74 changes: 74 additions & 0 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/PrintNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.TransactSql.ScriptDom;
using Microsoft.Xrm.Sdk;

namespace MarkMpn.Sql4Cds.Engine.ExecutionPlan
{
class PrintNode : BaseNode, IDmlQueryExecutionPlanNode
{
private int _executionCount;
private readonly Timer _timer = new Timer();
private Func<Entity, IDictionary<string, object>, IQueryExecutionOptions, object> _expression;

public override int ExecutionCount => _executionCount;

public override TimeSpan Duration => _timer.Duration;

[Browsable(false)]
public string Sql { get; set; }

[Browsable(false)]
public int Index { get; set; }

[Browsable(false)]
public int Length { get; set; }

[Category("Print")]
[Description("The value to print")]
public ScalarExpression Expression { get; set; }

public override void AddRequiredColumns(IDictionary<string, DataSource> dataSources, IDictionary<string, DataTypeReference> parameterTypes, IList<string> requiredColumns)
{
}

public object Clone()
{
return new PrintNode
{
Expression = Expression,
_expression = _expression
};
}

public string Execute(IDictionary<string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary<string, DataTypeReference> parameterTypes, IDictionary<string, object> parameterValues, out int recordsAffected)
{
_executionCount++;
recordsAffected = -1;

using (_timer.Run())
{
var value = (SqlString)_expression(null, parameterValues, options);

if (value.IsNull)
return null;

return value.Value;
}
}

public IRootExecutionPlanNodeInternal FoldQuery(IDictionary<string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary<string, DataTypeReference> parameterTypes, IList<OptimizerHint> hints)
{
_expression = Expression.Compile(null, parameterTypes);
return this;
}

public override IEnumerable<IExecutionPlanNode> GetSources()
{
return Array.Empty<IExecutionPlanNode>();
}
}
}
31 changes: 31 additions & 0 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlanBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ private void ConvertStatement(TSqlStatement statement, ExecutionPlanOptimizer op
plans = new[] { ConvertIfStatement(ifStmt, optimizer) };
else if (statement is WhileStatement whileStmt)
plans = new[] { ConvertWhileStatement(whileStmt, optimizer) };
else if (statement is PrintStatement print)
plans = new[] { ConvertPrintStatement(print, optimizer) };
else
throw new NotSupportedQueryFragmentException("Unsupported statement", statement);

Expand All @@ -159,6 +161,35 @@ private void ConvertStatement(TSqlStatement statement, ExecutionPlanOptimizer op
}
}

private IRootExecutionPlanNodeInternal ConvertPrintStatement(PrintStatement print, ExecutionPlanOptimizer optimizer)
{
// Check if the value is a simple expression or requires a query. Subqueries are not allowed
var subqueryVisitor = new ScalarSubqueryVisitor();
print.Expression.Accept(subqueryVisitor);

if (subqueryVisitor.Subqueries.Count > 0)
throw new NotSupportedQueryFragmentException("Subqueries are not allowed in this context. Only scalar expressions are allowed.", print.Expression);

// Check the expression for errors. Ensure it can be converted to a string
var expr = print.Expression;

if (print.Expression.GetType(null, null, _parameterTypes) != typeof(string))
{
expr = new ConvertCall
{
DataType = typeof(SqlString).ToSqlType(),
Parameter = print.Expression
};

expr.GetType(null, null, _parameterTypes);
}

return new PrintNode
{
Expression = expr
};
}

private IRootExecutionPlanNodeInternal ConvertIfWhileStatement(ConditionalNodeType type, BooleanExpression predicate, TSqlStatement trueStatement, TSqlStatement falseStatement, ExecutionPlanOptimizer optimizer)
{
// Check if the predicate is a simple expression or requires a query
Expand Down
1 change: 1 addition & 0 deletions MarkMpn.Sql4Cds.Engine/MarkMpn.Sql4Cds.Engine.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ExecutionPlan\NodeSchema.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExecutionPlan\OffsetFetchNode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExecutionPlan\PartitionedAggregateNode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExecutionPlan\PrintNode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExecutionPlan\QueryExecutionException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExecutionPlan\RetrieveTotalRecordCountNode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExecutionPlan\RevertNode.cs" />
Expand Down

0 comments on commit 5577a11

Please sign in to comment.