Class SqlExpressionConvertVisitor
- Namespace
- LinqToDB.Internal.SqlProvider
- Assembly
- linq2db.dll
public class SqlExpressionConvertVisitor : SqlQueryVisitor
- Inheritance
-
objectSqlExpressionConvertVisitor
- Derived
- Inherited Members
- Extension Methods
Constructors
SqlExpressionConvertVisitor(bool)
public SqlExpressionConvertVisitor(bool allowModify)
Parameters
allowModifybool
Fields
NullabilityContext
protected NullabilityContext NullabilityContext
Field Value
OptimizationContext
protected OptimizationContext OptimizationContext
Field Value
StandardLikeCharactersToEscape
protected static readonly string[] StandardLikeCharactersToEscape
Field Value
- string[]
VisitQueries
protected bool VisitQueries
Field Value
Properties
CanLowerIntervalDifference
Whether an elapsed date difference can be lowered to a value here.
public virtual bool CanLowerIntervalDifference { get; }
Property Value
Remarks
Declared beside the lowering it describes, and read by the member translator through
ITranslationContext.ProviderFlags. The translator has to ask before it builds anything, because
a difference it does not build stays an ordinary .NET subtraction and is computed on materialisation -
and by the time this visitor runs, the read expression is already bound to its columns, so there is no
going back.
CanLowerIntervalPart
Whether a member of an elapsed date difference can be lowered. Defaults to whatever the difference itself can do.
public virtual bool CanLowerIntervalPart { get; }
Property Value
Remarks
Separate because one provider has only this half: Access counts elapsed units well enough to answer
TotalHours, but its DateDiff is a 32-bit count that overflows once scaled to ticks, so
the interval never becomes a value there.
CanLowerIntervalShift
Whether LowerTemporalArithmetic(SqlTemporalArithmeticExpression) can express a date shifted by an interval at all.
public virtual bool CanLowerIntervalShift { get; }
Property Value
Remarks
Read by the member translator so a declared duration added to a date is declined while the expression is still being built, rather than reaching the builder as a node nothing can render - a refusal there has no client-side fallback left. The default follows what the base implementation needs: it spends the amount through ShiftDate(SqlIntervalUnit, ISqlExpression, ISqlExpression) at FinestDateUnit, so a provider that names no finest unit cannot lower one.
Only the declared half is declined early. A shift by a computed difference is left to be built,
because start + (end - start) cancels against the difference it came from and asks the provider
for nothing - refusing it here would sink a query that works everywhere.
CanMeasureDifferenceInTicks
Whether an elapsed date difference can become a tick count here. Defaults to yes.
public virtual bool CanMeasureDifferenceInTicks { get; }
Property Value
Remarks
A provider answering no has no way to reach a unit finer than IntervalResolution at all:
ElapsedTicks(SqlIntervalDifferenceExpression) leaves it nothing to divide, and its own difference function cannot name such
a unit either. Access is the case - its DateDiff is a 32-bit count that overflows once scaled to
ticks.
Read by the member translator, which declines to build a total in a unit finer than the resolution when this is false. Left to be built, that node reaches the SQL builder, which can only throw - and a throw there takes with it the projection's fall back to .NET, the one place the member can still be answered exactly. A total is otherwise left alone, because a coarser measurement makes one quantised rather than meaningless, which is why this is asked rather than the resolution alone.
CanWrapWindowOrderByConstant
Whether the scalar subquery WrapWindowOrderByConstant(ISqlExpression) builds is parseable as a window sort
key here. DuckDB is the exception: it takes one in a plain window but not inside a RANGE frame,
and it accepts the bare constant there instead, so it keeps the caller's key rather than the stand-in.
protected virtual bool CanWrapWindowOrderByConstant { get; }
Property Value
ConcatRequiresExplicitStringCast
When true (default), ConvertConcat(SqlConcatExpression) wraps every non-string
operand in an explicit CAST(... AS VARCHAR(N)) before adding it to the concat chain.
Required for providers whose concat operator is + (SQL Server pre-2025, SqlCe,
Access) — SQL-standard data-type precedence would otherwise try to coerce
string operands to the non-string side's type. Most providers whose final concat operator
is || (PostgreSQL / Oracle / SQLite / SAP HANA / DuckDB / Firebird / DB2 / Informix /
SQL Server 2025+) or CONCAT(...) function (MySQL / ClickHouse) auto-coerce
non-string operands and override this to false for cleaner SQL.
Sybase ASE is the exception: it emits || but keeps this true,
since ASE requires an explicit convert() for non-character operands under both
+ and ||.
protected virtual bool ConcatRequiresExplicitStringCast { get; }
Property Value
DataOptions
protected DataOptions DataOptions { get; }
Property Value
ElapsedTicksResolveMembers
Whether ElapsedTicks(SqlIntervalDifferenceExpression) is fine enough to answer the individual members, or only the difference taken as a whole. Defaults to yes.
protected virtual bool ElapsedTicksResolveMembers { get; }
Property Value
Remarks
A tick count answers every member with far less SQL, but only where it resolves what the provider
stores. Access counts seconds while an OLE Automation date holds fractions of one, so its count can sit
a second from the truth - enough to move Hours across a boundary - and counting each unit with
the correction against the actual dates stays exact there. The tick count still answers the difference
itself, which has no other form.
EvaluationContext
protected EvaluationContext EvaluationContext { get; }
Property Value
Factory
protected ISqlExpressionFactory Factory { get; }
Property Value
FinestDateUnit
Finest unit this provider can count date boundaries in, used for the fractional part of a
Total* member. null means totals of a date difference are not supported.
protected virtual SqlIntervalUnit? FinestDateUnit { get; }
Property Value
Remarks
Only ever applied to a window shorter than one of the requested units, so it cannot overflow however far apart the two dates are - which is what makes a total expressible at all. Taking the whole difference in this unit would overflow: a century in ticks is more than long holds.
IntervalResolution
The finest unit this provider can actually resolve when it measures elapsed time. Defaults to Tick - no loss.
public virtual SqlIntervalUnit IntervalResolution { get; }
Property Value
Remarks
Distinct from FinestDateUnit, which names the finest unit the provider's own difference
function counts in. A provider may measure elapsed time some other way and still be limited: SQLite goes
through julianday, whose double holds about 47 microseconds of a Julian day number, so its
measurement is rounded to the millisecond however the value is stored.
Read by the member translator, which declines to build a component in a unit finer than this. Such a component is not merely less precise - it is identically zero, because the count it is taken from was rounded to a coarser unit first. Declining leaves the member to .NET, which has both dates and can answer exactly.
IsInsidePredicate
protected bool IsInsidePredicate { get; }
Property Value
LikeCharactersToEscape
Characters with special meaning in LIKE predicate (defined by LikeCharactersToEscape) that should be escaped to be used as matched character.
Default: ["%", "_", "?", "*", "#", "[", "]"].
public virtual string[] LikeCharactersToEscape { get; }
Property Value
- string[]
LikeEscapeCharacter
Escape sequence/character to escape special characters in LIKE predicate (defined by LikeCharactersToEscape).
Default: "~".
public virtual string LikeEscapeCharacter { get; }
Property Value
LikeIsEscapeSupported
public virtual bool LikeIsEscapeSupported { get; }
Property Value
LikePatternParameterSupport
public virtual bool LikePatternParameterSupport { get; }
Property Value
LikeValueParameterSupport
public virtual bool LikeValueParameterSupport { get; }
Property Value
LikeWildcardCharacter
public virtual string LikeWildcardCharacter { get; }
Property Value
MappingSchema
protected MappingSchema MappingSchema { get; }
Property Value
SqlProviderFlags
protected SqlProviderFlags SqlProviderFlags { get; }
Property Value
SupportsBooleanInColumn
protected virtual bool SupportsBooleanInColumn { get; }
Property Value
SupportsDistinctAsExistsIntersect
protected virtual bool SupportsDistinctAsExistsIntersect { get; }
Property Value
SupportsNullIf
protected virtual bool SupportsNullIf { get; }
Property Value
SupportsNullInColumn
protected virtual bool SupportsNullInColumn { get; }
Property Value
Methods
Add(ISqlExpression, ISqlExpression, Type)
public ISqlExpression Add(ISqlExpression expr1, ISqlExpression expr2, Type type)
Parameters
expr1ISqlExpressionexpr2ISqlExpressiontypeType
Returns
Add(ISqlExpression, int)
public ISqlExpression Add(ISqlExpression expr1, int value)
Parameters
expr1ISqlExpressionvalueint
Returns
Add<T>(ISqlExpression, ISqlExpression)
public ISqlExpression Add<T>(ISqlExpression expr1, ISqlExpression expr2)
Parameters
expr1ISqlExpressionexpr2ISqlExpression
Returns
Type Parameters
T
Cleanup()
Resets visitor to initial state.
public override void Cleanup()
Convert(OptimizationContext, NullabilityContext, IQueryElement, bool)
public virtual IQueryElement Convert(OptimizationContext optimizationContext, NullabilityContext nullabilityContext, IQueryElement element, bool visitQueries)
Parameters
optimizationContextOptimizationContextnullabilityContextNullabilityContextelementIQueryElementvisitQueriesbool
Returns
ConvertBetweenPredicate(Between)
public virtual ISqlPredicate ConvertBetweenPredicate(SqlPredicate.Between between)
Parameters
betweenSqlPredicate.Between
Returns
ConvertBooleanToCase(ISqlExpression, DbDataType)
protected ISqlExpression ConvertBooleanToCase(ISqlExpression expr, DbDataType toType)
Parameters
exprISqlExpressiontoTypeDbDataType
Returns
ConvertCaseItem(CaseItem)
protected virtual SqlCaseExpression.CaseItem ConvertCaseItem(SqlCaseExpression.CaseItem newElement)
Parameters
newElementSqlCaseExpression.CaseItem
Returns
ConvertCastToPredicate(SqlCastExpression)
public virtual IQueryElement ConvertCastToPredicate(SqlCastExpression castExpression)
Parameters
castExpressionSqlCastExpression
Returns
ConvertCoalesce(SqlCoalesceExpression)
public virtual ISqlExpression ConvertCoalesce(SqlCoalesceExpression element)
Parameters
elementSqlCoalesceExpression
Returns
ConvertCoalesceToBinaryFunc(SqlCoalesceExpression, string, bool)
protected ISqlExpression ConvertCoalesceToBinaryFunc(SqlCoalesceExpression coalesce, string funcName, bool supportsParameters = true)
Parameters
coalesceSqlCoalesceExpressionfuncNamestringsupportsParametersbool
Returns
ConvertConcat(SqlConcatExpression)
public virtual ISqlExpression ConvertConcat(SqlConcatExpression element)
Parameters
elementSqlConcatExpression
Returns
ConvertConversion(SqlCastExpression)
Implements SqlCastExpression conversion.
protected virtual ISqlExpression ConvertConversion(SqlCastExpression cast)
Parameters
castSqlCastExpression
Returns
ConvertExprExprPredicate(ExprExpr)
public virtual IQueryElement ConvertExprExprPredicate(SqlPredicate.ExprExpr predicate)
Parameters
predicateSqlPredicate.ExprExpr
Returns
ConvertIsDistinctPredicate(IsDistinct)
public IQueryElement ConvertIsDistinctPredicate(SqlPredicate.IsDistinct predicate)
Parameters
predicateSqlPredicate.IsDistinct
Returns
ConvertIsDistinctPredicateAsIntersect(IsDistinct)
protected virtual IQueryElement ConvertIsDistinctPredicateAsIntersect(SqlPredicate.IsDistinct predicate)
Parameters
predicateSqlPredicate.IsDistinct
Returns
ConvertLikePredicate(Like)
public virtual ISqlPredicate ConvertLikePredicate(SqlPredicate.Like predicate)
Parameters
predicateSqlPredicate.Like
Returns
ConvertRowExprExpr(ExprExpr, EvaluationContext)
protected ISqlPredicate ConvertRowExprExpr(SqlPredicate.ExprExpr predicate, EvaluationContext context)
Parameters
predicateSqlPredicate.ExprExprcontextEvaluationContext
Returns
ConvertRowInList(InList)
protected virtual ISqlPredicate ConvertRowInList(SqlPredicate.InList predicate)
Parameters
predicateSqlPredicate.InList
Returns
ConvertSearchStringPredicate(SearchString)
public virtual ISqlPredicate ConvertSearchStringPredicate(SqlPredicate.SearchString predicate)
Parameters
predicateSqlPredicate.SearchString
Returns
ConvertSearchStringPredicateViaLike(SearchString)
protected ISqlPredicate ConvertSearchStringPredicateViaLike(SqlPredicate.SearchString predicate)
Parameters
predicateSqlPredicate.SearchString
Returns
ConvertSqlBinaryExpression(SqlBinaryExpression)
public virtual IQueryElement ConvertSqlBinaryExpression(SqlBinaryExpression element)
Parameters
elementSqlBinaryExpression
Returns
ConvertSqlCaseExpression(SqlCaseExpression)
protected virtual ISqlExpression ConvertSqlCaseExpression(SqlCaseExpression element)
Parameters
elementSqlCaseExpression
Returns
ConvertSqlCondition(SqlConditionExpression)
protected virtual ISqlExpression ConvertSqlCondition(SqlConditionExpression element)
Parameters
elementSqlConditionExpression
Returns
ConvertSqlExpression(SqlExpression)
public virtual ISqlExpression ConvertSqlExpression(SqlExpression element)
Parameters
elementSqlExpression
Returns
ConvertSqlExtendedFunction(SqlExtendedFunction)
public virtual ISqlExpression ConvertSqlExtendedFunction(SqlExtendedFunction func)
Parameters
funcSqlExtendedFunction
Returns
ConvertSqlFunction(SqlFunction)
public virtual ISqlExpression ConvertSqlFunction(SqlFunction func)
Parameters
funcSqlFunction
Returns
ConvertSqlUnaryExpression(SqlUnaryExpression)
public virtual ISqlExpression ConvertSqlUnaryExpression(SqlUnaryExpression element)
Parameters
elementSqlUnaryExpression
Returns
ConvertToBooleanSearchCondition(ISqlExpression)
protected SqlSearchCondition ConvertToBooleanSearchCondition(ISqlExpression expression)
Parameters
expressionISqlExpression
Returns
CountDateBoundaries(SqlIntervalUnit, ISqlExpression, ISqlExpression)
Counts unit boundaries crossed between two date/time values - the provider's DATEDIFF.
protected virtual ISqlExpression? CountDateBoundaries(SqlIntervalUnit unit, ISqlExpression start, ISqlExpression end)
Parameters
unitSqlIntervalUnitstartISqlExpressionendISqlExpression
Returns
Remarks
Boundary counting, not elapsed time. It is deliberately the wrong answer on its own: it is the cheap starting estimate that the anchor correction turns into the elapsed count, and being a count of whole units it cannot overflow the way a fine-grained difference over the same range would.
CreateLikeEscapeCharacter()
public virtual ISqlExpression CreateLikeEscapeCharacter()
Returns
Dec(ISqlExpression)
public ISqlExpression Dec(ISqlExpression expr1)
Parameters
expr1ISqlExpression
Returns
Div(ISqlExpression, ISqlExpression, Type)
public ISqlExpression Div(ISqlExpression expr1, ISqlExpression expr2, Type type)
Parameters
expr1ISqlExpressionexpr2ISqlExpressiontypeType
Returns
Div(ISqlExpression, int)
public ISqlExpression Div(ISqlExpression expr1, int value)
Parameters
expr1ISqlExpressionvalueint
Returns
Div<T>(ISqlExpression, ISqlExpression)
public ISqlExpression Div<T>(ISqlExpression expr1, ISqlExpression expr2)
Parameters
expr1ISqlExpressionexpr2ISqlExpression
Returns
Type Parameters
T
ElapsedTicks(SqlIntervalDifferenceExpression)
Elapsed ticks between two date/time values, exactly.
protected virtual ISqlExpression? ElapsedTicks(SqlIntervalDifferenceExpression element)
Parameters
elementSqlIntervalDifferenceExpression
Returns
- ISqlExpression
null when the provider has no exact form, leaving the expression untranslated.
Remarks
The one quantity that has to be a tick count rather than any equivalent duration, because Ticks asks for it by name. Unlike the other members it is not a count of whole units that anchoring can correct, so a provider answers it or does not - one that cannot produce it exactly, at the resolution its own date type stores, returns null rather than approximating.
The default derives it from the counting primitives, so a provider that has those needs nothing more: whole elapsed days, plus the remainder counted in FinestDateUnit. Neither part can overflow - the day count is small for any range a TimeSpan can hold, and the remainder is measured across a window shorter than one day - which is what makes this preferable to counting the whole range in a fine unit. A provider with a single exact expression for the difference overrides it with that instead.
EscapeLikeCharacters(ISqlExpression, ref ISqlExpression?)
public virtual ISqlExpression EscapeLikeCharacters(ISqlExpression expression, ref ISqlExpression? escape)
Parameters
expressionISqlExpressionescapeISqlExpression
Returns
EscapeLikeCharacters(string, string)
public virtual string EscapeLikeCharacters(string str, string escape)
Parameters
Returns
EscapeLikePattern(string)
Implements LIKE pattern escaping logic for provider without ESCAPE clause support (LikeIsEscapeSupported is false). Default logic prefix characters from LikeCharactersToEscape with LikeEscapeCharacter.
protected virtual string EscapeLikePattern(string str)
Parameters
strstringRaw pattern value.
Returns
- string
Escaped pattern value.
FloorBeforeConvert(SqlCastExpression)
protected SqlCastExpression FloorBeforeConvert(SqlCastExpression cast)
Parameters
castSqlCastExpression
Returns
GetMaxDisplaySize(DbDataType)
protected virtual int? GetMaxDisplaySize(DbDataType type)
Parameters
typeDbDataType
Returns
- int?
GetMaxLength(DbDataType)
protected virtual int? GetMaxLength(DbDataType type)
Parameters
typeDbDataType
Returns
- int?
GetMaxPrecision(DbDataType)
protected virtual int? GetMaxPrecision(DbDataType type)
Parameters
typeDbDataType
Returns
- int?
GetMaxScale(DbDataType)
protected virtual int? GetMaxScale(DbDataType type)
Parameters
typeDbDataType
Returns
- int?
Inc(ISqlExpression)
public ISqlExpression Inc(ISqlExpression expr1)
Parameters
expr1ISqlExpression
Returns
IsDateDataOffsetType(DbDataType)
protected static bool IsDateDataOffsetType(DbDataType dataType)
Parameters
dataTypeDbDataType
Returns
IsDateDataType(DbDataType, string)
protected static bool IsDateDataType(DbDataType dataType, string typeName)
Parameters
dataTypeDbDataTypetypeNamestring
Returns
IsDateTime2Type(DbDataType, string)
protected static bool IsDateTime2Type(DbDataType dataType, string typeName)
Parameters
dataTypeDbDataTypetypeNamestring
Returns
IsDateTimeType(DbDataType, string)
protected static bool IsDateTimeType(DbDataType dataType, string typeName)
Parameters
dataTypeDbDataTypetypeNamestring
Returns
IsOrderDependentWindowFunction(string)
The window functions no provider will order implicitly: a rank has to know what it is ranking, and
LAG/LEAD have to know which row counts as the neighbour. Every provider that enforces an
ORDER BY requirement at all covers at least these.
protected static bool IsOrderDependentWindowFunction(string functionName)
Parameters
functionNamestring
Returns
Remarks
NTILE and FIRST_VALUE/LAST_VALUE are deliberately left out, because providers genuinely
disagree on them: SQL Server and SAP HANA demand an ordering for all three, Oracle, DB2 and Informix for
NTILE alone, ClickHouse for NTILE and nothing else, MariaDB for none of them. So is
ROW_NUMBER, which IsWindowOrderByRequired(SqlExtendedFunction) answers from a provider flag. Each override
adds the ones its own dialect needs.
IsSmallDateTimeType(DbDataType, string)
protected static bool IsSmallDateTimeType(DbDataType dataType, string typeName)
Parameters
dataTypeDbDataTypetypeNamestring
Returns
IsTimeDataType(DbDataType)
protected static bool IsTimeDataType(DbDataType dataType)
Parameters
dataTypeDbDataType
Returns
IsWindowOrderByRequired(SqlExtendedFunction)
Whether the provider insists on an ORDER BY inside this function's OVER clause. The default
is false: standard SQL leaves the ordering optional, and most providers follow it.
protected virtual bool IsWindowOrderByRequired(SqlExtendedFunction func)
Parameters
funcSqlExtendedFunction
Returns
Remarks
Two things are answered here. ROW_NUMBER, from the flag that already states this very thing for the
row-number paging emulation. And a frame whose boundaries are read off the sort key - a GROUPS frame,
or a RANGE frame with a value offset - which the standard defines in terms of that key, so every
dialect enforces it and no provider may opt out. Which functions carry the requirement is another
matter entirely: providers disagree, so that part is left to the overrides, and PostgreSQL, SQLite, DuckDB,
Firebird, YDB and MySQL 8 add nothing at all. YDB in particular must stay that way, since it cannot parse a
scalar subquery as a sort key - it is safe here only because it rejects RANGE and GROUPS frames
at translation, so the frame arm below can never fire for it.
LowerIntervalDifference(SqlIntervalDifferenceExpression)
Lowers End - Start into the elapsed time as a value, in whatever form the read path turns back
into a TimeSpan.
protected virtual ISqlExpression? LowerIntervalDifference(SqlIntervalDifferenceExpression element)
Parameters
elementSqlIntervalDifferenceExpression
Returns
- ISqlExpression
null when the provider has no exact form, leaving the expression untranslated.
Remarks
Over integral storage that form is the tick count, which is the default. A provider with a native interval type overrides this to produce one instead - the value is the same duration either way, and which representation is used is exactly the provider's business.
LowerIntervalPart(SqlIntervalPartExpression)
Lowers an interval part to SQL. The default uses IntervalLowering's integral-storage strategy; providers with a native interval type override this to use it instead.
protected virtual ISqlExpression? LowerIntervalPart(SqlIntervalPartExpression element)
Parameters
elementSqlIntervalPartExpression
Returns
- ISqlExpression
null when the part cannot be produced exactly, leaving it untranslated.
LowerTemporalArithmetic(SqlTemporalArithmeticExpression)
Lowers a date/time value shifted by an interval.
protected virtual ISqlExpression? LowerTemporalArithmetic(SqlTemporalArithmeticExpression element)
Parameters
elementSqlTemporalArithmeticExpression
Returns
- ISqlExpression
null when the provider cannot express the shift.
Remarks
There is no default. A provider with a native interval type applies the operator directly; one that
lowered the interval to a tick count has to spend that count through its own DATEADD, whose
argument is usually a 32-bit integer and so cannot take ticks in one step.
Left alone, the node reaches the builder and is refused by name. That is the point of it existing: the generic binary handling would otherwise put a plain operator between a date and a number, which a database evaluates into something that still looks like a date.
Mul(ISqlExpression, ISqlExpression, Type)
public ISqlExpression Mul(ISqlExpression expr1, ISqlExpression expr2, Type type)
Parameters
expr1ISqlExpressionexpr2ISqlExpressiontypeType
Returns
Mul(ISqlExpression, int)
public ISqlExpression Mul(ISqlExpression expr1, int value)
Parameters
expr1ISqlExpressionvalueint
Returns
Mul<T>(ISqlExpression, ISqlExpression)
public ISqlExpression Mul<T>(ISqlExpression expr1, ISqlExpression expr2)
Parameters
expr1ISqlExpressionexpr2ISqlExpression
Returns
Type Parameters
T
NormalizeWindowOrderBy(SqlExtendedFunction)
Brings a window's ORDER BY to a form every dialect accepts.
protected ISqlExpression NormalizeWindowOrderBy(SqlExtendedFunction func)
Parameters
funcSqlExtendedFunction
Returns
Remarks
A sort key that holds the same value for every row orders nothing - all rows tie - so it is dropped, the
same treatment BuildOrderByClause(SelectQuery) gives a statement's ORDER BY. That
alone settles the dialects that refuse a constant there (SQL Server and SAP HANA reject any constant;
MySQL 8 reads an integer one as a legacy column position), because none of them ever sees one inside
OVER. Only that ordering is rewritten here: the same node's WITHIN GROUP and KEEP
order lists are built elsewhere and still carry a constant through verbatim.
Dropping can empty the clause, which is a problem of its own: on some providers a ranking function needs
an ORDER BY, and so does a frame. IsWindowOrderByRequired(SqlExtendedFunction) says
when that is the case, and the first key the caller wrote comes back wrapped by
WrapWindowOrderByConstant(ISqlExpression) - keeping the value, the direction and the NULLS position it was
given - rather than being replaced by an invented one. Only a window that arrived with no ORDER BY
at all has nothing to carry over, and that one gets a plain 1; Sql.Window.DefineWindow with
only a PARTITION BY reaches a ranking function that way.
Optimize(IQueryElement)
protected IQueryElement Optimize(IQueryElement element)
Parameters
elementIQueryElement
Returns
RemoveNullValues(SqlCoalesceExpression)
Removes NULL-literal operands from a COALESCE operand list — a literal NULL can never be the
value COALESCE returns, so it is redundant. Returns the sole surviving operand when only one
remains, a reduced SqlCoalesceExpression over the survivors when several remain,
or the last operand when every operand is a NULL literal. Returns element
unchanged when it has no NULL-literal operands.
Shared so providers that fold COALESCE into a native construct (Informix Nvl, Access
IIF) apply the same normalization the base ConvertCoalesce(SqlCoalesceExpression) does before
folding; otherwise a no-op guard such as Coalesce(x, NULL) folds to Nvl(x, NULL)
/ IIF(x IS NULL, NULL, x) (issue #5531).
protected ISqlExpression RemoveNullValues(SqlCoalesceExpression element)
Parameters
elementSqlCoalesceExpression
Returns
RowComparisonFallback(Operator, SqlRowExpression, SqlRowExpression, EvaluationContext)
protected ISqlPredicate RowComparisonFallback(SqlPredicate.Operator op, SqlRowExpression row1, SqlRowExpression row2, EvaluationContext context)
Parameters
opSqlPredicate.Operatorrow1SqlRowExpressionrow2SqlRowExpressioncontextEvaluationContext
Returns
RowIsNullFallback(SqlRowExpression, bool)
protected ISqlPredicate RowIsNullFallback(SqlRowExpression row, bool isNot)
Parameters
rowSqlRowExpressionisNotbool
Returns
ShiftDate(SqlIntervalUnit, ISqlExpression, ISqlExpression)
Shifts a date/time value by a whole number of units - the provider's DATEADD.
protected virtual ISqlExpression? ShiftDate(SqlIntervalUnit unit, ISqlExpression amount, ISqlExpression date)
Parameters
unitSqlIntervalUnitamountISqlExpressiondateISqlExpression
Returns
Remarks
Must be exact: the anchor correction in IntervalLowering shifts by a computed count and compares the result against the original, so an approximate shift would produce an off-by-one count.
Sub(ISqlExpression, ISqlExpression, Type)
public ISqlExpression Sub(ISqlExpression expr1, ISqlExpression expr2, Type type)
Parameters
expr1ISqlExpressionexpr2ISqlExpressiontypeType
Returns
Sub(ISqlExpression, int)
public ISqlExpression Sub(ISqlExpression expr1, int value)
Parameters
expr1ISqlExpressionvalueint
Returns
Sub<T>(ISqlExpression, ISqlExpression)
public ISqlExpression Sub<T>(ISqlExpression expr1, ISqlExpression expr2)
Parameters
expr1ISqlExpressionexpr2ISqlExpression
Returns
Type Parameters
T
TruncateDivide(ISqlExpression, long)
Integer division truncating toward zero.
protected virtual ISqlExpression TruncateDivide(ISqlExpression value, long divisor)
Parameters
valueISqlExpressiondivisorlong
Returns
Remarks
The default is the division itself, which is what dividing two integers means in SQL and matches CLR integer division on negatives. A provider whose division is not integral overrides it - MySQL and DuckDB return a fraction, Access has no integer division at all - and so does one whose truncation is spelled its own way.
TruncateRemainder(ISqlExpression, long)
Remainder of the same truncating division, which is what % means on integers in most databases
and what the CLR operator means.
protected virtual ISqlExpression TruncateRemainder(ISqlExpression value, long divisor)
Parameters
valueISqlExpressiondivisorlong
Returns
Remarks
Kept separate from TruncateDivide(ISqlExpression, long) because composing it out of one - as
value - trunc(value / divisor) * divisor - repeats the value three times, and the components of
an interval nest two of these, so the repetition multiplies. A provider whose remainder disagrees on
negatives, or that spells it as a function, overrides this.
TryConvertToValue(ISqlExpression, EvaluationContext)
protected ISqlExpression TryConvertToValue(ISqlExpression expr, EvaluationContext context)
Parameters
exprISqlExpressioncontextEvaluationContext
Returns
Visit(IQueryElement?)
Visitor dispatch method.
public override IQueryElement? Visit(IQueryElement? element)
Parameters
elementIQueryElement
Returns
VisitBetweenPredicate(Between)
protected override IQueryElement VisitBetweenPredicate(SqlPredicate.Between predicate)
Parameters
predicateSqlPredicate.Between
Returns
VisitCaseItem(CaseItem)
protected override SqlCaseExpression.CaseItem VisitCaseItem(SqlCaseExpression.CaseItem element)
Parameters
elementSqlCaseExpression.CaseItem
Returns
VisitExprExprPredicate(ExprExpr)
protected override IQueryElement VisitExprExprPredicate(SqlPredicate.ExprExpr predicate)
Parameters
predicateSqlPredicate.ExprExpr
Returns
VisitExprPredicate(Expr)
protected override IQueryElement VisitExprPredicate(SqlPredicate.Expr predicate)
Parameters
predicateSqlPredicate.Expr
Returns
VisitInListPredicate(InList)
protected override IQueryElement VisitInListPredicate(SqlPredicate.InList predicate)
Parameters
predicateSqlPredicate.InList
Returns
VisitInSubQueryPredicate(InSubQuery)
protected override IQueryElement VisitInSubQueryPredicate(SqlPredicate.InSubQuery predicate)
Parameters
predicateSqlPredicate.InSubQuery
Returns
VisitIsDistinctPredicate(IsDistinct)
protected override IQueryElement VisitIsDistinctPredicate(SqlPredicate.IsDistinct predicate)
Parameters
predicateSqlPredicate.IsDistinct
Returns
VisitIsNullPredicate(IsNull)
protected override IQueryElement VisitIsNullPredicate(SqlPredicate.IsNull predicate)
Parameters
predicateSqlPredicate.IsNull
Returns
VisitLikePredicate(Like)
protected override IQueryElement VisitLikePredicate(SqlPredicate.Like predicate)
Parameters
predicateSqlPredicate.Like
Returns
VisitNotPredicate(Not)
protected override IQueryElement VisitNotPredicate(SqlPredicate.Not predicate)
Parameters
predicateSqlPredicate.Not
Returns
VisitSearchStringPredicate(SearchString)
protected override IQueryElement VisitSearchStringPredicate(SqlPredicate.SearchString predicate)
Parameters
predicateSqlPredicate.SearchString
Returns
VisitSqlBinaryExpression(SqlBinaryExpression)
protected override IQueryElement VisitSqlBinaryExpression(SqlBinaryExpression element)
Parameters
elementSqlBinaryExpression
Returns
VisitSqlCaseExpression(SqlCaseExpression)
protected override IQueryElement VisitSqlCaseExpression(SqlCaseExpression element)
Parameters
elementSqlCaseExpression
Returns
VisitSqlCastExpression(SqlCastExpression)
protected override IQueryElement VisitSqlCastExpression(SqlCastExpression element)
Parameters
elementSqlCastExpression
Returns
VisitSqlCoalesceExpression(SqlCoalesceExpression)
protected override IQueryElement VisitSqlCoalesceExpression(SqlCoalesceExpression element)
Parameters
elementSqlCoalesceExpression
Returns
VisitSqlColumnExpression(SqlColumn, ISqlExpression)
Visit of column expression from owner table. For column references visitor see VisitSqlColumnReference(SqlColumn)
protected override ISqlExpression VisitSqlColumnExpression(SqlColumn column, ISqlExpression expression)
Parameters
columnSqlColumnexpressionISqlExpression
Returns
VisitSqlColumnReference(SqlColumn)
Used to visit columns as references in other expressions. Actual visit of table column happens in VisitSqlColumnExpression(SqlColumn, ISqlExpression).
protected override IQueryElement VisitSqlColumnReference(SqlColumn element)
Parameters
elementSqlColumn
Returns
VisitSqlCompareToExpression(SqlCompareToExpression)
protected override IQueryElement VisitSqlCompareToExpression(SqlCompareToExpression element)
Parameters
elementSqlCompareToExpression
Returns
VisitSqlConcatExpression(SqlConcatExpression)
protected override IQueryElement VisitSqlConcatExpression(SqlConcatExpression element)
Parameters
elementSqlConcatExpression
Returns
VisitSqlConditionExpression(SqlConditionExpression)
protected override IQueryElement VisitSqlConditionExpression(SqlConditionExpression element)
Parameters
elementSqlConditionExpression
Returns
VisitSqlExpression(SqlExpression)
protected override IQueryElement VisitSqlExpression(SqlExpression element)
Parameters
elementSqlExpression
Returns
VisitSqlExtendedFunction(SqlExtendedFunction)
Visitor for SqlExtendedFunction.
protected override IQueryElement VisitSqlExtendedFunction(SqlExtendedFunction element)
Parameters
elementSqlExtendedFunction
Returns
VisitSqlExtendedFunctionPartition(SqlExtendedFunction, ISqlExpression)
Visit of a single PARTITION BY item of SqlExtendedFunction. Unlike ORDER BY
items, which are SqlWindowOrderItem and get their own visitor, partition items are plain
expressions — this hook gives derived visitors the owning function as context.
protected override ISqlExpression VisitSqlExtendedFunctionPartition(SqlExtendedFunction function, ISqlExpression partition)
Parameters
functionSqlExtendedFunctionpartitionISqlExpression
Returns
VisitSqlFieldReference(SqlField)
Visitor for SqlField reference from query expressions.
protected override IQueryElement VisitSqlFieldReference(SqlField element)
Parameters
elementSqlField
Returns
VisitSqlFunction(SqlFunction)
protected override IQueryElement VisitSqlFunction(SqlFunction element)
Parameters
elementSqlFunction
Returns
VisitSqlFunctionArgument(SqlFunctionArgument)
protected override IQueryElement VisitSqlFunctionArgument(SqlFunctionArgument element)
Parameters
elementSqlFunctionArgument
Returns
VisitSqlGroupByItem(ISqlExpression)
protected override ISqlExpression VisitSqlGroupByItem(ISqlExpression element)
Parameters
elementISqlExpression
Returns
VisitSqlInlinedSqlExpression(SqlInlinedSqlExpression)
protected override IQueryElement VisitSqlInlinedSqlExpression(SqlInlinedSqlExpression element)
Parameters
elementSqlInlinedSqlExpression
Returns
VisitSqlInlinedToSqlExpression(SqlInlinedToSqlExpression)
protected override IQueryElement VisitSqlInlinedToSqlExpression(SqlInlinedToSqlExpression element)
Parameters
elementSqlInlinedToSqlExpression
Returns
VisitSqlIntervalDifferenceExpression(SqlIntervalDifferenceExpression)
protected override IQueryElement VisitSqlIntervalDifferenceExpression(SqlIntervalDifferenceExpression element)
Parameters
elementSqlIntervalDifferenceExpression
Returns
VisitSqlIntervalExpression(SqlIntervalExpression)
protected override IQueryElement VisitSqlIntervalExpression(SqlIntervalExpression element)
Parameters
elementSqlIntervalExpression
Returns
Remarks
Over integral storage an interval is its stored amount, so the node simply disappears here. The read path turns the amount back into a TimeSpan through the operand's column descriptor, which GetColumnDescriptor(ISqlExpression?) reaches by looking through this node.
Which makes reaching that descriptor an invariant rather than a convenience: past this point the SQL value no longer says what unit it counts, so whatever wraps or rewrites it - a cast, a function, a projection into a derived table, a branch of a set operation - has to leave the descriptor reachable from the result. Where it does not, the statement stays valid and the value is read through the wrong conversion, which is the one failure the lowering cannot see for itself. BasicSqlBuilder refuses this node rather than rendering its operand for the same reason: a provider that never lowered it away should say so, not quietly emit a bare number where a duration was meant.
VisitSqlIntervalPartExpression(SqlIntervalPartExpression)
protected override IQueryElement VisitSqlIntervalPartExpression(SqlIntervalPartExpression element)
Parameters
elementSqlIntervalPartExpression
Returns
VisitSqlJoinedTable(SqlJoinedTable)
protected override IQueryElement VisitSqlJoinedTable(SqlJoinedTable element)
Parameters
elementSqlJoinedTable
Returns
VisitSqlOrderByItem(SqlOrderByItem)
protected override IQueryElement VisitSqlOrderByItem(SqlOrderByItem element)
Parameters
elementSqlOrderByItem
Returns
VisitSqlOutputClause(SqlOutputClause)
protected override IQueryElement VisitSqlOutputClause(SqlOutputClause element)
Parameters
elementSqlOutputClause
Returns
VisitSqlQuery(SelectQuery)
protected override IQueryElement VisitSqlQuery(SelectQuery selectQuery)
Parameters
selectQuerySelectQuery
Returns
VisitSqlSetExpression(SqlSetExpression)
protected override IQueryElement VisitSqlSetExpression(SqlSetExpression element)
Parameters
elementSqlSetExpression
Returns
VisitSqlTemporalArithmeticExpression(SqlTemporalArithmeticExpression)
protected override IQueryElement VisitSqlTemporalArithmeticExpression(SqlTemporalArithmeticExpression element)
Parameters
elementSqlTemporalArithmeticExpression
Returns
VisitSqlUnaryExpression(SqlUnaryExpression)
protected override IQueryElement VisitSqlUnaryExpression(SqlUnaryExpression element)
Parameters
elementSqlUnaryExpression
Returns
VisitSqlValue(SqlValue)
protected override IQueryElement VisitSqlValue(SqlValue element)
Parameters
elementSqlValue
Returns
VisitSqlWindowOrderItem(SqlWindowOrderItem)
protected override IQueryElement VisitSqlWindowOrderItem(SqlWindowOrderItem element)
Parameters
elementSqlWindowOrderItem
Returns
WrapBooleanCoalesceItems(SqlCoalesceExpression, IQueryElement, bool)
protected virtual SqlCoalesceExpression? WrapBooleanCoalesceItems(SqlCoalesceExpression element, IQueryElement newElement, bool forceConvert = false)
Parameters
elementSqlCoalesceExpressionnewElementIQueryElementforceConvertbool
Returns
WrapBooleanExpression(ISqlExpression, bool, bool, bool)
protected virtual ISqlExpression WrapBooleanExpression(ISqlExpression expr, bool includeFields, bool forceConvert = false, bool withNull = true)
Parameters
exprISqlExpressionincludeFieldsboolforceConvertboolwithNullbool
Returns
WrapColumnExpression(ISqlExpression)
protected virtual ISqlExpression WrapColumnExpression(ISqlExpression expr)
Parameters
exprISqlExpression
Returns
WrapWindowOrderByConstant(ISqlExpression)
Restates a constant window sort key as a scalar subquery, for a provider that demands an ORDER BY
but refuses a constant one.
protected virtual ISqlExpression WrapWindowOrderByConstant(ISqlExpression expression)
Parameters
expressionISqlExpression
Returns
Remarks
(SELECT 1) is constant in value but is not a constant expression, and that is the
distinction the strict dialects draw: it is the documented T-SQL spelling of "no meaningful ordering",
and Oracle and SAP HANA take it too, once their builders supply the dummy FROM a table-less query
needs there. Wrapping rather than substituting keeps whatever the caller passed - OrderBy(5) stays
a 5, and a captured local stays the parameter it became instead of vanishing from the command.
DoNotRemove stops query optimization from folding the subquery away.