Table of Contents

Class SqlExpressionConvertVisitor

Namespace
LinqToDB.Internal.SqlProvider
Assembly
linq2db.dll
public class SqlExpressionConvertVisitor : SqlQueryVisitor
Inheritance
object
SqlExpressionConvertVisitor
Derived
Inherited Members
Extension Methods

Constructors

SqlExpressionConvertVisitor(bool)

public SqlExpressionConvertVisitor(bool allowModify)

Parameters

allowModify bool

Fields

NullabilityContext

protected NullabilityContext NullabilityContext

Field Value

NullabilityContext

OptimizationContext

protected OptimizationContext OptimizationContext

Field Value

OptimizationContext

StandardLikeCharactersToEscape

protected static readonly string[] StandardLikeCharactersToEscape

Field Value

string[]

VisitQueries

protected bool VisitQueries

Field Value

bool

Properties

CanLowerIntervalDifference

Whether an elapsed date difference can be lowered to a value here.

public virtual bool CanLowerIntervalDifference { get; }

Property Value

bool

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

bool

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

bool

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

bool

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

bool

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

bool

DataOptions

protected DataOptions DataOptions { get; }

Property Value

DataOptions

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

bool

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

EvaluationContext

Factory

protected ISqlExpressionFactory Factory { get; }

Property Value

ISqlExpressionFactory

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

SqlIntervalUnit?

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

SqlIntervalUnit

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

bool

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

string

LikeIsEscapeSupported

Should be true for provider with LIKE ... ESCAPE modifier support. Default: true.

public virtual bool LikeIsEscapeSupported { get; }

Property Value

bool

LikePatternParameterSupport

public virtual bool LikePatternParameterSupport { get; }

Property Value

bool

LikeValueParameterSupport

public virtual bool LikeValueParameterSupport { get; }

Property Value

bool

LikeWildcardCharacter

public virtual string LikeWildcardCharacter { get; }

Property Value

string

MappingSchema

protected MappingSchema MappingSchema { get; }

Property Value

MappingSchema

SqlProviderFlags

protected SqlProviderFlags SqlProviderFlags { get; }

Property Value

SqlProviderFlags

SupportsBooleanInColumn

protected virtual bool SupportsBooleanInColumn { get; }

Property Value

bool

SupportsDistinctAsExistsIntersect

protected virtual bool SupportsDistinctAsExistsIntersect { get; }

Property Value

bool

SupportsNullIf

protected virtual bool SupportsNullIf { get; }

Property Value

bool

SupportsNullInColumn

protected virtual bool SupportsNullInColumn { get; }

Property Value

bool

Methods

Add(ISqlExpression, ISqlExpression, Type)

public ISqlExpression Add(ISqlExpression expr1, ISqlExpression expr2, Type type)

Parameters

expr1 ISqlExpression
expr2 ISqlExpression
type Type

Returns

ISqlExpression

Add(ISqlExpression, int)

public ISqlExpression Add(ISqlExpression expr1, int value)

Parameters

expr1 ISqlExpression
value int

Returns

ISqlExpression

Add<T>(ISqlExpression, ISqlExpression)

public ISqlExpression Add<T>(ISqlExpression expr1, ISqlExpression expr2)

Parameters

expr1 ISqlExpression
expr2 ISqlExpression

Returns

ISqlExpression

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

optimizationContext OptimizationContext
nullabilityContext NullabilityContext
element IQueryElement
visitQueries bool

Returns

IQueryElement

ConvertBetweenPredicate(Between)

public virtual ISqlPredicate ConvertBetweenPredicate(SqlPredicate.Between between)

Parameters

between SqlPredicate.Between

Returns

ISqlPredicate

ConvertBooleanToCase(ISqlExpression, DbDataType)

protected ISqlExpression ConvertBooleanToCase(ISqlExpression expr, DbDataType toType)

Parameters

expr ISqlExpression
toType DbDataType

Returns

ISqlExpression

ConvertCaseItem(CaseItem)

protected virtual SqlCaseExpression.CaseItem ConvertCaseItem(SqlCaseExpression.CaseItem newElement)

Parameters

newElement SqlCaseExpression.CaseItem

Returns

SqlCaseExpression.CaseItem

ConvertCastToPredicate(SqlCastExpression)

public virtual IQueryElement ConvertCastToPredicate(SqlCastExpression castExpression)

Parameters

castExpression SqlCastExpression

Returns

IQueryElement

ConvertCoalesce(SqlCoalesceExpression)

public virtual ISqlExpression ConvertCoalesce(SqlCoalesceExpression element)

Parameters

element SqlCoalesceExpression

Returns

ISqlExpression

ConvertCoalesceToBinaryFunc(SqlCoalesceExpression, string, bool)

protected ISqlExpression ConvertCoalesceToBinaryFunc(SqlCoalesceExpression coalesce, string funcName, bool supportsParameters = true)

Parameters

coalesce SqlCoalesceExpression
funcName string
supportsParameters bool

Returns

ISqlExpression

ConvertConcat(SqlConcatExpression)

public virtual ISqlExpression ConvertConcat(SqlConcatExpression element)

Parameters

element SqlConcatExpression

Returns

ISqlExpression

ConvertConversion(SqlCastExpression)

Implements SqlCastExpression conversion.

protected virtual ISqlExpression ConvertConversion(SqlCastExpression cast)

Parameters

cast SqlCastExpression

Returns

ISqlExpression

ConvertExprExprPredicate(ExprExpr)

public virtual IQueryElement ConvertExprExprPredicate(SqlPredicate.ExprExpr predicate)

Parameters

predicate SqlPredicate.ExprExpr

Returns

IQueryElement

ConvertIsDistinctPredicate(IsDistinct)

public IQueryElement ConvertIsDistinctPredicate(SqlPredicate.IsDistinct predicate)

Parameters

predicate SqlPredicate.IsDistinct

Returns

IQueryElement

ConvertIsDistinctPredicateAsIntersect(IsDistinct)

protected virtual IQueryElement ConvertIsDistinctPredicateAsIntersect(SqlPredicate.IsDistinct predicate)

Parameters

predicate SqlPredicate.IsDistinct

Returns

IQueryElement

ConvertLikePredicate(Like)

public virtual ISqlPredicate ConvertLikePredicate(SqlPredicate.Like predicate)

Parameters

predicate SqlPredicate.Like

Returns

ISqlPredicate

ConvertRowExprExpr(ExprExpr, EvaluationContext)

protected ISqlPredicate ConvertRowExprExpr(SqlPredicate.ExprExpr predicate, EvaluationContext context)

Parameters

predicate SqlPredicate.ExprExpr
context EvaluationContext

Returns

ISqlPredicate

ConvertRowInList(InList)

protected virtual ISqlPredicate ConvertRowInList(SqlPredicate.InList predicate)

Parameters

predicate SqlPredicate.InList

Returns

ISqlPredicate

ConvertSearchStringPredicate(SearchString)

public virtual ISqlPredicate ConvertSearchStringPredicate(SqlPredicate.SearchString predicate)

Parameters

predicate SqlPredicate.SearchString

Returns

ISqlPredicate

ConvertSearchStringPredicateViaLike(SearchString)

protected ISqlPredicate ConvertSearchStringPredicateViaLike(SqlPredicate.SearchString predicate)

Parameters

predicate SqlPredicate.SearchString

Returns

ISqlPredicate

ConvertSqlBinaryExpression(SqlBinaryExpression)

public virtual IQueryElement ConvertSqlBinaryExpression(SqlBinaryExpression element)

Parameters

element SqlBinaryExpression

Returns

IQueryElement

ConvertSqlCaseExpression(SqlCaseExpression)

protected virtual ISqlExpression ConvertSqlCaseExpression(SqlCaseExpression element)

Parameters

element SqlCaseExpression

Returns

ISqlExpression

ConvertSqlCondition(SqlConditionExpression)

protected virtual ISqlExpression ConvertSqlCondition(SqlConditionExpression element)

Parameters

element SqlConditionExpression

Returns

ISqlExpression

ConvertSqlExpression(SqlExpression)

public virtual ISqlExpression ConvertSqlExpression(SqlExpression element)

Parameters

element SqlExpression

Returns

ISqlExpression

ConvertSqlExtendedFunction(SqlExtendedFunction)

public virtual ISqlExpression ConvertSqlExtendedFunction(SqlExtendedFunction func)

Parameters

func SqlExtendedFunction

Returns

ISqlExpression

ConvertSqlFunction(SqlFunction)

public virtual ISqlExpression ConvertSqlFunction(SqlFunction func)

Parameters

func SqlFunction

Returns

ISqlExpression

ConvertSqlUnaryExpression(SqlUnaryExpression)

public virtual ISqlExpression ConvertSqlUnaryExpression(SqlUnaryExpression element)

Parameters

element SqlUnaryExpression

Returns

ISqlExpression

ConvertToBooleanSearchCondition(ISqlExpression)

protected SqlSearchCondition ConvertToBooleanSearchCondition(ISqlExpression expression)

Parameters

expression ISqlExpression

Returns

SqlSearchCondition

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

unit SqlIntervalUnit
start ISqlExpression
end ISqlExpression

Returns

ISqlExpression

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

ISqlExpression

Dec(ISqlExpression)

public ISqlExpression Dec(ISqlExpression expr1)

Parameters

expr1 ISqlExpression

Returns

ISqlExpression

Div(ISqlExpression, ISqlExpression, Type)

public ISqlExpression Div(ISqlExpression expr1, ISqlExpression expr2, Type type)

Parameters

expr1 ISqlExpression
expr2 ISqlExpression
type Type

Returns

ISqlExpression

Div(ISqlExpression, int)

public ISqlExpression Div(ISqlExpression expr1, int value)

Parameters

expr1 ISqlExpression
value int

Returns

ISqlExpression

Div<T>(ISqlExpression, ISqlExpression)

public ISqlExpression Div<T>(ISqlExpression expr1, ISqlExpression expr2)

Parameters

expr1 ISqlExpression
expr2 ISqlExpression

Returns

ISqlExpression

Type Parameters

T

ElapsedTicks(SqlIntervalDifferenceExpression)

Elapsed ticks between two date/time values, exactly.

protected virtual ISqlExpression? ElapsedTicks(SqlIntervalDifferenceExpression element)

Parameters

element SqlIntervalDifferenceExpression

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

expression ISqlExpression
escape ISqlExpression

Returns

ISqlExpression

EscapeLikeCharacters(string, string)

public virtual string EscapeLikeCharacters(string str, string escape)

Parameters

str string
escape string

Returns

string

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

str string

Raw pattern value.

Returns

string

Escaped pattern value.

FloorBeforeConvert(SqlCastExpression)

protected SqlCastExpression FloorBeforeConvert(SqlCastExpression cast)

Parameters

cast SqlCastExpression

Returns

SqlCastExpression

GetMaxDisplaySize(DbDataType)

protected virtual int? GetMaxDisplaySize(DbDataType type)

Parameters

type DbDataType

Returns

int?

GetMaxLength(DbDataType)

protected virtual int? GetMaxLength(DbDataType type)

Parameters

type DbDataType

Returns

int?

GetMaxPrecision(DbDataType)

protected virtual int? GetMaxPrecision(DbDataType type)

Parameters

type DbDataType

Returns

int?

GetMaxScale(DbDataType)

protected virtual int? GetMaxScale(DbDataType type)

Parameters

type DbDataType

Returns

int?

Inc(ISqlExpression)

public ISqlExpression Inc(ISqlExpression expr1)

Parameters

expr1 ISqlExpression

Returns

ISqlExpression

IsDateDataOffsetType(DbDataType)

protected static bool IsDateDataOffsetType(DbDataType dataType)

Parameters

dataType DbDataType

Returns

bool

IsDateDataType(DbDataType, string)

protected static bool IsDateDataType(DbDataType dataType, string typeName)

Parameters

dataType DbDataType
typeName string

Returns

bool

IsDateTime2Type(DbDataType, string)

protected static bool IsDateTime2Type(DbDataType dataType, string typeName)

Parameters

dataType DbDataType
typeName string

Returns

bool

IsDateTimeType(DbDataType, string)

protected static bool IsDateTimeType(DbDataType dataType, string typeName)

Parameters

dataType DbDataType
typeName string

Returns

bool

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

functionName string

Returns

bool

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

dataType DbDataType
typeName string

Returns

bool

IsTimeDataType(DbDataType)

protected static bool IsTimeDataType(DbDataType dataType)

Parameters

dataType DbDataType

Returns

bool

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

func SqlExtendedFunction

Returns

bool

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

element SqlIntervalDifferenceExpression

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

element SqlIntervalPartExpression

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

element SqlTemporalArithmeticExpression

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

expr1 ISqlExpression
expr2 ISqlExpression
type Type

Returns

ISqlExpression

Mul(ISqlExpression, int)

public ISqlExpression Mul(ISqlExpression expr1, int value)

Parameters

expr1 ISqlExpression
value int

Returns

ISqlExpression

Mul<T>(ISqlExpression, ISqlExpression)

public ISqlExpression Mul<T>(ISqlExpression expr1, ISqlExpression expr2)

Parameters

expr1 ISqlExpression
expr2 ISqlExpression

Returns

ISqlExpression

Type Parameters

T

NormalizeWindowOrderBy(SqlExtendedFunction)

Brings a window's ORDER BY to a form every dialect accepts.

protected ISqlExpression NormalizeWindowOrderBy(SqlExtendedFunction func)

Parameters

func SqlExtendedFunction

Returns

ISqlExpression

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

element IQueryElement

Returns

IQueryElement

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

element SqlCoalesceExpression

Returns

ISqlExpression

RowComparisonFallback(Operator, SqlRowExpression, SqlRowExpression, EvaluationContext)

protected ISqlPredicate RowComparisonFallback(SqlPredicate.Operator op, SqlRowExpression row1, SqlRowExpression row2, EvaluationContext context)

Parameters

op SqlPredicate.Operator
row1 SqlRowExpression
row2 SqlRowExpression
context EvaluationContext

Returns

ISqlPredicate

RowIsNullFallback(SqlRowExpression, bool)

protected ISqlPredicate RowIsNullFallback(SqlRowExpression row, bool isNot)

Parameters

row SqlRowExpression
isNot bool

Returns

ISqlPredicate

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

unit SqlIntervalUnit
amount ISqlExpression
date ISqlExpression

Returns

ISqlExpression

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

expr1 ISqlExpression
expr2 ISqlExpression
type Type

Returns

ISqlExpression

Sub(ISqlExpression, int)

public ISqlExpression Sub(ISqlExpression expr1, int value)

Parameters

expr1 ISqlExpression
value int

Returns

ISqlExpression

Sub<T>(ISqlExpression, ISqlExpression)

public ISqlExpression Sub<T>(ISqlExpression expr1, ISqlExpression expr2)

Parameters

expr1 ISqlExpression
expr2 ISqlExpression

Returns

ISqlExpression

Type Parameters

T

TruncateDivide(ISqlExpression, long)

Integer division truncating toward zero.

protected virtual ISqlExpression TruncateDivide(ISqlExpression value, long divisor)

Parameters

value ISqlExpression
divisor long

Returns

ISqlExpression

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

value ISqlExpression
divisor long

Returns

ISqlExpression

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

expr ISqlExpression
context EvaluationContext

Returns

ISqlExpression

Visit(IQueryElement?)

Visitor dispatch method.

public override IQueryElement? Visit(IQueryElement? element)

Parameters

element IQueryElement

Returns

IQueryElement

VisitBetweenPredicate(Between)

protected override IQueryElement VisitBetweenPredicate(SqlPredicate.Between predicate)

Parameters

predicate SqlPredicate.Between

Returns

IQueryElement

VisitCaseItem(CaseItem)

protected override SqlCaseExpression.CaseItem VisitCaseItem(SqlCaseExpression.CaseItem element)

Parameters

element SqlCaseExpression.CaseItem

Returns

SqlCaseExpression.CaseItem

VisitExprExprPredicate(ExprExpr)

protected override IQueryElement VisitExprExprPredicate(SqlPredicate.ExprExpr predicate)

Parameters

predicate SqlPredicate.ExprExpr

Returns

IQueryElement

VisitExprPredicate(Expr)

protected override IQueryElement VisitExprPredicate(SqlPredicate.Expr predicate)

Parameters

predicate SqlPredicate.Expr

Returns

IQueryElement

VisitInListPredicate(InList)

protected override IQueryElement VisitInListPredicate(SqlPredicate.InList predicate)

Parameters

predicate SqlPredicate.InList

Returns

IQueryElement

VisitInSubQueryPredicate(InSubQuery)

protected override IQueryElement VisitInSubQueryPredicate(SqlPredicate.InSubQuery predicate)

Parameters

predicate SqlPredicate.InSubQuery

Returns

IQueryElement

VisitIsDistinctPredicate(IsDistinct)

protected override IQueryElement VisitIsDistinctPredicate(SqlPredicate.IsDistinct predicate)

Parameters

predicate SqlPredicate.IsDistinct

Returns

IQueryElement

VisitIsNullPredicate(IsNull)

protected override IQueryElement VisitIsNullPredicate(SqlPredicate.IsNull predicate)

Parameters

predicate SqlPredicate.IsNull

Returns

IQueryElement

VisitLikePredicate(Like)

protected override IQueryElement VisitLikePredicate(SqlPredicate.Like predicate)

Parameters

predicate SqlPredicate.Like

Returns

IQueryElement

VisitNotPredicate(Not)

protected override IQueryElement VisitNotPredicate(SqlPredicate.Not predicate)

Parameters

predicate SqlPredicate.Not

Returns

IQueryElement

VisitSearchStringPredicate(SearchString)

protected override IQueryElement VisitSearchStringPredicate(SqlPredicate.SearchString predicate)

Parameters

predicate SqlPredicate.SearchString

Returns

IQueryElement

VisitSqlBinaryExpression(SqlBinaryExpression)

protected override IQueryElement VisitSqlBinaryExpression(SqlBinaryExpression element)

Parameters

element SqlBinaryExpression

Returns

IQueryElement

VisitSqlCaseExpression(SqlCaseExpression)

protected override IQueryElement VisitSqlCaseExpression(SqlCaseExpression element)

Parameters

element SqlCaseExpression

Returns

IQueryElement

VisitSqlCastExpression(SqlCastExpression)

protected override IQueryElement VisitSqlCastExpression(SqlCastExpression element)

Parameters

element SqlCastExpression

Returns

IQueryElement

VisitSqlCoalesceExpression(SqlCoalesceExpression)

protected override IQueryElement VisitSqlCoalesceExpression(SqlCoalesceExpression element)

Parameters

element SqlCoalesceExpression

Returns

IQueryElement

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

column SqlColumn
expression ISqlExpression

Returns

ISqlExpression

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

element SqlColumn

Returns

IQueryElement

VisitSqlCompareToExpression(SqlCompareToExpression)

protected override IQueryElement VisitSqlCompareToExpression(SqlCompareToExpression element)

Parameters

element SqlCompareToExpression

Returns

IQueryElement

VisitSqlConcatExpression(SqlConcatExpression)

protected override IQueryElement VisitSqlConcatExpression(SqlConcatExpression element)

Parameters

element SqlConcatExpression

Returns

IQueryElement

VisitSqlConditionExpression(SqlConditionExpression)

protected override IQueryElement VisitSqlConditionExpression(SqlConditionExpression element)

Parameters

element SqlConditionExpression

Returns

IQueryElement

VisitSqlExpression(SqlExpression)

protected override IQueryElement VisitSqlExpression(SqlExpression element)

Parameters

element SqlExpression

Returns

IQueryElement

VisitSqlExtendedFunction(SqlExtendedFunction)

Visitor for SqlExtendedFunction.

protected override IQueryElement VisitSqlExtendedFunction(SqlExtendedFunction element)

Parameters

element SqlExtendedFunction

Returns

IQueryElement

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

function SqlExtendedFunction
partition ISqlExpression

Returns

ISqlExpression

VisitSqlFieldReference(SqlField)

Visitor for SqlField reference from query expressions.

protected override IQueryElement VisitSqlFieldReference(SqlField element)

Parameters

element SqlField

Returns

IQueryElement

VisitSqlFunction(SqlFunction)

protected override IQueryElement VisitSqlFunction(SqlFunction element)

Parameters

element SqlFunction

Returns

IQueryElement

VisitSqlFunctionArgument(SqlFunctionArgument)

protected override IQueryElement VisitSqlFunctionArgument(SqlFunctionArgument element)

Parameters

element SqlFunctionArgument

Returns

IQueryElement

VisitSqlGroupByItem(ISqlExpression)

protected override ISqlExpression VisitSqlGroupByItem(ISqlExpression element)

Parameters

element ISqlExpression

Returns

ISqlExpression

VisitSqlInlinedSqlExpression(SqlInlinedSqlExpression)

protected override IQueryElement VisitSqlInlinedSqlExpression(SqlInlinedSqlExpression element)

Parameters

element SqlInlinedSqlExpression

Returns

IQueryElement

VisitSqlInlinedToSqlExpression(SqlInlinedToSqlExpression)

protected override IQueryElement VisitSqlInlinedToSqlExpression(SqlInlinedToSqlExpression element)

Parameters

element SqlInlinedToSqlExpression

Returns

IQueryElement

VisitSqlIntervalDifferenceExpression(SqlIntervalDifferenceExpression)

protected override IQueryElement VisitSqlIntervalDifferenceExpression(SqlIntervalDifferenceExpression element)

Parameters

element SqlIntervalDifferenceExpression

Returns

IQueryElement

VisitSqlIntervalExpression(SqlIntervalExpression)

protected override IQueryElement VisitSqlIntervalExpression(SqlIntervalExpression element)

Parameters

element SqlIntervalExpression

Returns

IQueryElement

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

element SqlIntervalPartExpression

Returns

IQueryElement

VisitSqlJoinedTable(SqlJoinedTable)

protected override IQueryElement VisitSqlJoinedTable(SqlJoinedTable element)

Parameters

element SqlJoinedTable

Returns

IQueryElement

VisitSqlOrderByItem(SqlOrderByItem)

protected override IQueryElement VisitSqlOrderByItem(SqlOrderByItem element)

Parameters

element SqlOrderByItem

Returns

IQueryElement

VisitSqlOutputClause(SqlOutputClause)

protected override IQueryElement VisitSqlOutputClause(SqlOutputClause element)

Parameters

element SqlOutputClause

Returns

IQueryElement

VisitSqlQuery(SelectQuery)

protected override IQueryElement VisitSqlQuery(SelectQuery selectQuery)

Parameters

selectQuery SelectQuery

Returns

IQueryElement

VisitSqlSetExpression(SqlSetExpression)

protected override IQueryElement VisitSqlSetExpression(SqlSetExpression element)

Parameters

element SqlSetExpression

Returns

IQueryElement

VisitSqlTemporalArithmeticExpression(SqlTemporalArithmeticExpression)

protected override IQueryElement VisitSqlTemporalArithmeticExpression(SqlTemporalArithmeticExpression element)

Parameters

element SqlTemporalArithmeticExpression

Returns

IQueryElement

VisitSqlUnaryExpression(SqlUnaryExpression)

protected override IQueryElement VisitSqlUnaryExpression(SqlUnaryExpression element)

Parameters

element SqlUnaryExpression

Returns

IQueryElement

VisitSqlValue(SqlValue)

protected override IQueryElement VisitSqlValue(SqlValue element)

Parameters

element SqlValue

Returns

IQueryElement

VisitSqlWindowOrderItem(SqlWindowOrderItem)

protected override IQueryElement VisitSqlWindowOrderItem(SqlWindowOrderItem element)

Parameters

element SqlWindowOrderItem

Returns

IQueryElement

WrapBooleanCoalesceItems(SqlCoalesceExpression, IQueryElement, bool)

protected virtual SqlCoalesceExpression? WrapBooleanCoalesceItems(SqlCoalesceExpression element, IQueryElement newElement, bool forceConvert = false)

Parameters

element SqlCoalesceExpression
newElement IQueryElement
forceConvert bool

Returns

SqlCoalesceExpression

WrapBooleanExpression(ISqlExpression, bool, bool, bool)

protected virtual ISqlExpression WrapBooleanExpression(ISqlExpression expr, bool includeFields, bool forceConvert = false, bool withNull = true)

Parameters

expr ISqlExpression
includeFields bool
forceConvert bool
withNull bool

Returns

ISqlExpression

WrapColumnExpression(ISqlExpression)

protected virtual ISqlExpression WrapColumnExpression(ISqlExpression expr)

Parameters

expr ISqlExpression

Returns

ISqlExpression

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

expression ISqlExpression

Returns

ISqlExpression

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.