Skip to content

Commit

Permalink
Merge pull request #490 from MarkMpn/v9-fixes
Browse files Browse the repository at this point in the history
V9 fixes
  • Loading branch information
MarkMpn authored Jun 20, 2024
2 parents d4f1403 + c8dde05 commit bba57b5
Show file tree
Hide file tree
Showing 31 changed files with 1,058 additions and 295 deletions.
2 changes: 2 additions & 0 deletions MarkMpn.Sql4Cds.Engine.Tests/FakeXrmDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public FakeXrmDataSource()

public override bool ColumnComparisonAvailable => _columnComparisonAvailable;

public override bool CrossTableColumnComparisonAvailable => _columnComparisonAvailable;

public override bool OrderByEntityNameAvailable => _orderByEntityNameAvailable;

public override List<JoinOperator> JoinOperatorsAvailable => _joinOperators;
Expand Down
6 changes: 6 additions & 0 deletions MarkMpn.Sql4Cds.Engine/AttributeMetadataCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ protected void OnMetadataLoading(MetadataLoadingEventArgs args)
{
MetadataLoading?.Invoke(this, args);
}

/// <inheritdoc/>
public IEnumerable<EntityMetadata> GetAllEntities()
{
return _metadata.Values;
}
}

/// <summary>
Expand Down
19 changes: 18 additions & 1 deletion MarkMpn.Sql4Cds.Engine/DataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Collections.Generic;
using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;
using Microsoft.Crm.Sdk.Messages;
using System.Linq;

#if NETCOREAPP
using Microsoft.PowerPlatform.Dataverse.Client;
#else
Expand Down Expand Up @@ -87,6 +89,7 @@ public DataSource(IOrganizationService org, IAttributeMetadataCache metadata, IT

JoinOperatorsAvailable = joinOperators;
ColumnComparisonAvailable = version >= new Version("9.1.0.19251");
CrossTableColumnComparisonAvailable = version >= new Version("9.2");
OrderByEntityNameAvailable = version >= new Version("9.1.0.25249");
}

Expand Down Expand Up @@ -132,6 +135,11 @@ public DataSource()
/// </summary>
public virtual bool ColumnComparisonAvailable { get; }

/// <summary>
/// Indicates if the server supports column comparison conditions across tables in FetchXML
/// </summary>
public virtual bool CrossTableColumnComparisonAvailable { get; }

/// <summary>
/// Indicates if the server supports ordering by link-entities in FetchXML
/// </summary>
Expand Down Expand Up @@ -206,7 +214,11 @@ private Collation LoadDefaultCollation()
{
ColumnSet = new ColumnSet("localeid")
};
var org = Connection.RetrieveMultiple(qry).Entities[0];
var org = Connection.RetrieveMultiple(qry).Entities.FirstOrDefault();

if (org == null)
return Collation.USEnglish;

var lcid = org.GetAttributeValue<int>("localeid");

// Collation options are set based on the default language. Most are CI/AI but a few are not
Expand Down Expand Up @@ -241,5 +253,10 @@ private Collation LoadDefaultCollation()

return new Collation(lcid, !ci, !ai);
}

public DataSource Clone()
{
return (DataSource)MemberwiseClone();
}
}
}
14 changes: 9 additions & 5 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/BaseDataNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,10 @@ private bool TranslateFetchXMLCriteria(NodeCompilationContext context, DataSourc
if (!String.IsNullOrEmpty(attr2?.AttributeOf))
return false;

// If we need to use cross-table column comparisons, check the server supports it
if (!entityAlias.Equals(entityAlias2, StringComparison.OrdinalIgnoreCase) && !dataSource.CrossTableColumnComparisonAvailable)
return false;

// We can use valueof="alias.attribute", but the alias of the root entity isn't visible. Swap the comparison round
// so that it can be added to the root entity and reference the value from the link entity.
if (!entityAlias.Equals(entityAlias2, StringComparison.OrdinalIgnoreCase) && entityAlias2.Equals(targetEntityAlias, StringComparison.OrdinalIgnoreCase))
Expand Down Expand Up @@ -596,8 +600,8 @@ private bool TranslateFetchXMLCriteria(NodeCompilationContext context, DataSourc
},
new condition
{
entityname = StandardizeAlias(entityAlias, targetEntityAlias, items),
attribute = RemoveAttributeAlias(attrName2, entityAlias, targetEntityAlias, items),
entityname = StandardizeAlias(entityAlias2, targetEntityAlias, items),
attribute = RemoveAttributeAlias(attrName2, entityAlias2, targetEntityAlias, items),
@operator = @operator.notnull
}
}
Expand All @@ -607,7 +611,7 @@ private bool TranslateFetchXMLCriteria(NodeCompilationContext context, DataSourc
else if (op == @operator.eq && type == BooleanComparisonType.IsNotDistinctFrom)
{
// FetchXML eq operator does not match records where both fields are null.
// This matches the logic for =, but IS DISTINCT FROM also allows nulls to match
// This matches the logic for =, but IS NOT DISTINCT FROM also allows nulls to match
filter = new filter
{
type = filterType.or,
Expand All @@ -627,8 +631,8 @@ private bool TranslateFetchXMLCriteria(NodeCompilationContext context, DataSourc
},
new condition
{
entityname = StandardizeAlias(entityAlias, targetEntityAlias, items),
attribute = RemoveAttributeAlias(attrName2, entityAlias, targetEntityAlias, items),
entityname = StandardizeAlias(entityAlias2, targetEntityAlias, items),
attribute = RemoveAttributeAlias(attrName2, entityAlias2, targetEntityAlias, items),
@operator = @operator.@null
}
}
Expand Down
18 changes: 5 additions & 13 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/ExecuteMessageNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private void SetOutputSchema(DataSource dataSource, Message message, TSqlFragmen
if (message.OutputParameters.All(f => f.IsScalarType()))
{
foreach (var value in message.OutputParameters)
AddSchemaColumn(value.Name, SqlTypeConverter.NetToSqlType(value.Type).ToSqlType(dataSource)); // TODO: How are OSV and ER fields represented?
AddSchemaColumn(value.Name, value.GetSqlDataType(dataSource));
}
else
{
Expand Down Expand Up @@ -206,16 +206,8 @@ private void SetOutputSchema(DataSource dataSource, Message message, TSqlFragmen
AddSchemaColumn(attrMetadata.LogicalName, attrMetadata.GetAttributeSqlType(dataSource, false));

// Add standard virtual attributes
if (attrMetadata is EnumAttributeMetadata || attrMetadata is BooleanAttributeMetadata)
AddSchemaColumn(attrMetadata.LogicalName + "name", DataTypeHelpers.NVarChar(FetchXmlScan.LabelMaxLength, dataSource.DefaultCollation, CollationLabel.CoercibleDefault));

if (attrMetadata is LookupAttributeMetadata lookup)
{
AddSchemaColumn(attrMetadata.LogicalName + "name", DataTypeHelpers.NVarChar(lookup.Targets == null || lookup.Targets.Length == 0 ? 100 : lookup.Targets.Select(e => ((StringAttributeMetadata)dataSource.Metadata[e].Attributes.SingleOrDefault(a => a.LogicalName == dataSource.Metadata[e].PrimaryNameAttribute))?.MaxLength ?? 100).Max(), dataSource.DefaultCollation, CollationLabel.CoercibleDefault));

if (lookup.Targets?.Length != 1 && lookup.AttributeType != AttributeTypeCode.PartyList)
AddSchemaColumn(attrMetadata.LogicalName + "type", DataTypeHelpers.NVarChar(MetadataExtensions.EntityLogicalNameMaxLength, dataSource.DefaultCollation, CollationLabel.CoercibleDefault));
}
foreach (var virtualAttr in attrMetadata.GetVirtualAttributes(dataSource, false))
AddSchemaColumn(attrMetadata.LogicalName + virtualAttr.Suffix, virtualAttr.DataType);
}

if (audit)
Expand Down Expand Up @@ -564,7 +556,7 @@ public static ExecuteMessageNode FromMessage(SchemaObjectFunctionTableReference
var f = expectedInputParameters[i];
var sourceExpression = tvf.Parameters[i];
sourceExpression.GetType(context, out var sourceType);
var expectedType = SqlTypeConverter.NetToSqlType(f.Type).ToSqlType(context.PrimaryDataSource);
var expectedType = f.GetSqlDataType(context.PrimaryDataSource);

if (!SqlTypeConverter.CanChangeTypeImplicit(sourceType, expectedType))
throw new NotSupportedQueryFragmentException(Sql4CdsError.TypeClash(tvf.Parameters[f.Position], sourceType, expectedType));
Expand Down Expand Up @@ -666,7 +658,7 @@ public static ExecuteMessageNode FromMessage(ExecutableProcedureReference sproc,

var sourceExpression = sproc.Parameters[i].ParameterValue;
sourceExpression.GetType(context, out var sourceType);
var expectedType = SqlTypeConverter.NetToSqlType(targetParam.Type).ToSqlType(context.PrimaryDataSource);
var expectedType = targetParam.GetSqlDataType(context.PrimaryDataSource);

if (!SqlTypeConverter.CanChangeTypeImplicit(sourceType, expectedType))
{
Expand Down
18 changes: 2 additions & 16 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/FetchXmlScan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ public InvalidPagingException(string message) : base(message)
}
}

public const int LabelMaxLength = 200;
private static readonly XmlSerializer _serializer = new XmlSerializer(typeof(FetchXml.FetchType));

private Dictionary<string, List<ParameterizedCondition>> _parameterizedConditions;
Expand Down Expand Up @@ -1290,21 +1289,8 @@ private void AddSchemaAttribute(DataSource dataSource, ColumnList schema, Dictio
return;

// Add standard virtual attributes
if (attrMetadata is MultiSelectPicklistAttributeMetadata)
AddSchemaAttribute(schema, aliases, AddSuffix(fullName, "name"), (attrMetadata.LogicalName + "name").EscapeIdentifier(), DataTypeHelpers.NVarChar(Int32.MaxValue, dataSource.DefaultCollation, CollationLabel.Implicit), notNull);
else if (attrMetadata is EnumAttributeMetadata || attrMetadata is BooleanAttributeMetadata)
AddSchemaAttribute(schema, aliases, AddSuffix(fullName, "name"), (attrMetadata.LogicalName + "name").EscapeIdentifier(), DataTypeHelpers.NVarChar(LabelMaxLength, dataSource.DefaultCollation, CollationLabel.Implicit), notNull);

if (attrMetadata is LookupAttributeMetadata lookup)
{
AddSchemaAttribute(schema, aliases, AddSuffix(fullName, "name"), (attrMetadata.LogicalName + "name").EscapeIdentifier(), DataTypeHelpers.NVarChar(lookup.Targets == null || lookup.Targets.Length == 0 ? 100 : lookup.Targets.Select(e => (dataSource.Metadata[e].Attributes.SingleOrDefault(a => a.LogicalName == dataSource.Metadata[e].PrimaryNameAttribute) as StringAttributeMetadata)?.MaxLength ?? 100).Max(), dataSource.DefaultCollation, CollationLabel.Implicit), notNull);

if (lookup.Targets?.Length != 1 && lookup.AttributeType != AttributeTypeCode.PartyList)
AddSchemaAttribute(schema, aliases, AddSuffix(fullName, "type"), (attrMetadata.LogicalName + "type").EscapeIdentifier(), DataTypeHelpers.NVarChar(MetadataExtensions.EntityLogicalNameMaxLength, dataSource.DefaultCollation, CollationLabel.Implicit), notNull); ;

if (lookup.Targets != null && lookup.Targets.Any(logicalName => dataSource.Metadata[logicalName].DataProviderId == DataProviders.ElasticDataProvider))
AddSchemaAttribute(schema, aliases, AddSuffix(fullName, "pid"), (attrMetadata.LogicalName + "pid").EscapeIdentifier(), DataTypeHelpers.NVarChar(100, dataSource.DefaultCollation, CollationLabel.Implicit), false);
}
foreach (var virtualAttr in attrMetadata.GetVirtualAttributes(dataSource, false))
AddSchemaAttribute(schema, aliases, AddSuffix(fullName, virtualAttr.Suffix), (attrMetadata.LogicalName + virtualAttr.Suffix).EscapeIdentifier(), virtualAttr.DataType, virtualAttr.NotNull ?? notNull);
}

private void AddSchemaAttribute(ColumnList schema, Dictionary<string, IReadOnlyList<string>> aliases, string fullName, string simpleName, DataTypeReference type, bool notNull)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.SqlServer.TransactSql.ScriptDom;
using Microsoft.Xrm.Sdk;
Expand Down Expand Up @@ -106,6 +107,10 @@ private static int FaultCodeToSqlError(OrganizationServiceFault fault)
}
}

var match = Regex.Match(fault.Message, "Sql Number: (\\d+)");
if (match.Success)
return Int32.Parse(match.Groups[1].Value);

return 10337;
}
}
Expand Down
2 changes: 2 additions & 0 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/RaiseErrorNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public void Execute(NodeExecutionContext context, out int recordsAffected, out s
{
try
{
_executionCount++;

var ecc = new ExpressionCompilationContext(context, null, null);
var eec = new ExpressionExecutionContext(context);

Expand Down
2 changes: 2 additions & 0 deletions MarkMpn.Sql4Cds.Engine/ExecutionPlan/ThrowNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public override IEnumerable<IExecutionPlanNode> GetSources()

public void Execute(NodeExecutionContext context, out int recordsAffected, out string message)
{
_executionCount++;

if (ErrorNumber == null)
context.Log(context.Error);

Expand Down
9 changes: 8 additions & 1 deletion MarkMpn.Sql4Cds.Engine/IAttributeMetadataCache.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Xrm.Sdk.Metadata;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk.Metadata;

namespace MarkMpn.Sql4Cds.Engine
{
Expand Down Expand Up @@ -88,5 +89,11 @@ public interface IAttributeMetadataCache
/// Returns a list of entity logical names that are enabled for recycle bin access
/// </summary>
string[] RecycleBinEntities { get; }

/// <summary>
/// Gets a list of all available entities for autocomplete
/// </summary>
/// <returns>A list of all available entities for autocomplete</returns>
IEnumerable<EntityMetadata> GetAllEntities();
}
}
6 changes: 6 additions & 0 deletions MarkMpn.Sql4Cds.Engine/MessageCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MarkMpn.Sql4Cds.Engine.ExecutionPlan;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
Expand Down Expand Up @@ -429,5 +430,10 @@ public bool IsScalarType()

return false;
}

/// <summary>
/// Returns the SQL data type for the parameter
/// </summary>
public Microsoft.SqlServer.TransactSql.ScriptDom.DataTypeReference GetSqlDataType(DataSource dataSource) => SqlTypeConverter.NetToSqlType(Type).ToSqlType(dataSource);
}
}
6 changes: 6 additions & 0 deletions MarkMpn.Sql4Cds.Engine/MetaMetadataCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,5 +304,11 @@ public bool TryGetMinimalData(string logicalName, out EntityMetadata metadata)

/// <inheritdoc/>
public string[] RecycleBinEntities => _inner.RecycleBinEntities;

/// <inheritdoc/>
public IEnumerable<EntityMetadata> GetAllEntities()
{
return _inner.GetAllEntities().Concat(_customMetadata.Values);
}
}
}
Loading

0 comments on commit bba57b5

Please sign in to comment.