public override (string, QxType, QxNullity) CompileNative(QxCompilationContext ctx) { if (string.IsNullOrWhiteSpace(Property)) { // Developer mistake throw new InvalidOperationException($"Bug: Invoking {nameof(CompileNative)} on a {nameof(QueryexColumnAccess)} that does not have a property."); } var propName = Property; // (A) Calculate Nullity (the entire path foreign keys + the final property must be all NOT NULL) bool pathNotNull = true; var join = ctx.Joins; foreach (var step in Path) { var navPropDesc = join.EntityDescriptor.NavigationProperty(step); pathNotNull = pathNotNull && navPropDesc.ForeignKey.IsNotNull; join = join[step]; } var propDesc = join.EntityDescriptor.Property(propName); if (propDesc == null) { throw new QueryException($"Property '{propName}' does not exist on type {join.EntityDescriptor.Name}."); } QxNullity nullity = pathNotNull && propDesc.IsNotNull ? QxNullity.NotNull : QxNullity.Nullable; // (B) Calculate the type QxType type; var propType = Nullable.GetUnderlyingType(propDesc.Type) ?? propDesc.Type; switch (propType.Name) { case nameof(Char): case nameof(String): type = QxType.String; break; case nameof(Byte): case nameof(SByte): case nameof(Int16): case nameof(UInt16): case nameof(Int32): case nameof(UInt32): case nameof(Int64): case nameof(UInt64): case nameof(Single): case nameof(Double): case nameof(Decimal): type = QxType.Numeric; break; case nameof(Boolean): type = QxType.Bit; break; case nameof(DateTime): type = propDesc.IncludesTime ? QxType.DateTime : QxType.Date; break; case nameof(DateTimeOffset): type = QxType.DateTimeOffset; break; case nameof(HierarchyId): type = QxType.HierarchyId; break; case nameof(Geography): type = QxType.Geography; break; default: if (propDesc is NavigationPropertyDescriptor || propDesc is CollectionPropertyDescriptor) { throw new QueryException($"A column access cannot terminate with a navigation property like {propDesc.Name}."); } else { // Developer mistake throw new InvalidOperationException($"[Bug] Could not map type {propType.Name} to a {nameof(QxType)}"); // Future proofing } } // (C) Calculate the SQL var sql = $"[{join.Symbol}].[{propName}]"; // Return the result return(sql, type, nullity); }
public override bool TryCompile(QxType targetType, QxCompilationContext ctx, out string resultSql, out QxNullity resultNullity) { // This here is merely an optimization if (Operator == "+") { if ((targetType == QxType.String && // String concatenation Left.TryCompile(QxType.String, ctx, out string leftSql, out QxNullity leftNullity) && Right.TryCompile(QxType.String, ctx, out string rightSql, out QxNullity rightNullity)) || (targetType == QxType.Numeric && // Numeric Left.TryCompile(QxType.Numeric, ctx, out leftSql, out leftNullity) && Right.TryCompile(QxType.Numeric, ctx, out rightSql, out rightNullity))) { resultNullity = leftNullity | rightNullity; resultSql = $"({leftSql} + {rightSql})"; return(true); } else { // No other types are possible for + resultNullity = default; resultSql = null; return(false); } }
public override bool TryCompile(QxType targetType, QxCompilationContext ctx, out string resultSql, out QxNullity resultNullity) { switch (targetType) { case QxType.Date: if (DateTime.TryParse(Value, out DateTime d)) { d = d.Date; // Remove the time component string varDef = $"N'{d:yyyy-MM-dd}'"; string varName = ctx.Variables.AddVariable("DATE", varDef); resultSql = $"@{varName}"; resultNullity = QxNullity.NotNull; return(true); } else { resultSql = null; resultNullity = default; return(false); } case QxType.DateTime: if (DateTime.TryParse(Value, out DateTime dt)) { string varDef = $"N'{dt:yyyy-MM-ddTHH:mm:ss.ff}'"; string varName = ctx.Variables.AddVariable("DATETIME2(2)", varDef); resultSql = $"@{varName}"; resultNullity = QxNullity.NotNull; return(true); } else { resultSql = null; resultNullity = default; return(false); } case QxType.DateTimeOffset: if (DateTimeOffset.TryParse(Value, out DateTimeOffset dto)) { string varDef = $"N'{dto:o}'"; string varName = ctx.Variables.AddVariable("DATETIMEOFFSET(7)", varDef); resultSql = $"@{varName}"; resultNullity = QxNullity.NotNull; return(true); } else { resultSql = null; resultNullity = default; return(false); } } return(base.TryCompile(targetType, ctx, out resultSql, out resultNullity)); }
public override bool TryCompile(QxType targetType, QxCompilationContext ctx, out string resultSql, out QxNullity resultNullity) { string nameLower = Name?.ToLower(); switch (nameLower) { case "min": case "max": { var(arg1, conditionSql) = AggregationParameters(ctx); if (arg1.TryCompile(targetType, ctx, out string expSql, out resultNullity)) { resultSql = AggregationCompile(expSql, conditionSql); return(true); }