Table of Contents

Class ClickHouseSqlExpressionConvertVisitor

Namespace
LinqToDB.Internal.DataProvider.ClickHouse
Assembly
linq2db.dll
public class ClickHouseSqlExpressionConvertVisitor : SqlExpressionConvertVisitor
Inheritance
object
ClickHouseSqlExpressionConvertVisitor
Inherited Members
Extension Methods

Constructors

ClickHouseSqlExpressionConvertVisitor(bool, ClickHouseOptions)

public ClickHouseSqlExpressionConvertVisitor(bool allowModify, ClickHouseOptions providerOptions)

Parameters

allowModify bool
providerOptions ClickHouseOptions

Properties

CanLowerIntervalDifference

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

public override 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.

CanLowerIntervalShift

Whether LowerTemporalArithmetic(SqlTemporalArithmeticExpression) can express a date shifted by an interval at all.

public override 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.

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 override bool ConcatRequiresExplicitStringCast { 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 override string[] LikeCharactersToEscape { get; }

Property Value

string[]

LikeEscapeCharacter

Escape sequence/character to escape special characters in LIKE predicate (defined by LikeCharactersToEscape). Default: "~".

public override string LikeEscapeCharacter { get; }

Property Value

string

LikeIsEscapeSupported

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

public override bool LikeIsEscapeSupported { get; }

Property Value

bool

SupportsNullInColumn

protected override bool SupportsNullInColumn { get; }

Property Value

bool

Methods

ConvertLikePredicate(Like)

public override ISqlPredicate ConvertLikePredicate(SqlPredicate.Like predicate)

Parameters

predicate SqlPredicate.Like

Returns

ISqlPredicate

ConvertSearchStringPredicate(SearchString)

public override ISqlPredicate ConvertSearchStringPredicate(SqlPredicate.SearchString predicate)

Parameters

predicate SqlPredicate.SearchString

Returns

ISqlPredicate

ConvertSqlBinaryExpression(SqlBinaryExpression)

public override IQueryElement ConvertSqlBinaryExpression(SqlBinaryExpression element)

Parameters

element SqlBinaryExpression

Returns

IQueryElement

ConvertSqlExtendedFunction(SqlExtendedFunction)

public override ISqlExpression ConvertSqlExtendedFunction(SqlExtendedFunction func)

Parameters

func SqlExtendedFunction

Returns

ISqlExpression

ConvertSqlFunction(SqlFunction)

public override ISqlExpression ConvertSqlFunction(SqlFunction func)

Parameters

func SqlFunction

Returns

ISqlExpression

ConvertSqlUnaryExpression(SqlUnaryExpression)

public override ISqlExpression ConvertSqlUnaryExpression(SqlUnaryExpression element)

Parameters

element SqlUnaryExpression

Returns

ISqlExpression

ElapsedTicks(SqlIntervalDifferenceExpression)

Elapsed ticks from the nanosecond timestamps, divided by a hundred.

protected override ISqlExpression? ElapsedTicks(SqlIntervalDifferenceExpression element)

Parameters

element SqlIntervalDifferenceExpression

Returns

ISqlExpression

Remarks

linq2db maps date/time values to DateTime64(7), which is a tick exactly, so every nanosecond value here is a whole multiple of a hundred and the division is exact. date_diff is not used because its finest unit is the second.

Nanoseconds in an long reach from 1678 to 2262, narrower than what a DateTime64(7) column itself holds. That is the same boundary the millisecond form of DateAdd already works within on this provider.

The span between two of them is narrower still, because the subtraction is taken in nanoseconds as well: two endpoints inside that window can differ by more than an long holds, so the usable span is about 292 years rather than the 584 the endpoints cover. ClickHouse wraps instead of raising, so a wider difference comes back as a plausible-looking wrong number.

IsWindowOrderByRequired(SqlExtendedFunction)

NTILE alone, and by way of the frame rather than the ordering: unordered, the window defaults to a frame ClickHouse will not accept for it (Unsupported window frame type for function 'NTILE'). Giving it a sort key restores the default frame it wants.

protected override bool IsWindowOrderByRequired(SqlExtendedFunction func)

Parameters

func SqlExtendedFunction

Returns

bool

LowerTemporalArithmetic(SqlTemporalArithmeticExpression)

Shifts by the interval expressed in nanoseconds, the unit that matches what a tick is.

protected override ISqlExpression? LowerTemporalArithmetic(SqlTemporalArithmeticExpression element)

Parameters

element SqlTemporalArithmeticExpression

Returns

ISqlExpression

Remarks

toIntervalNanosecond takes an expression, so the count needs no decomposition. Scaling ticks up by a hundred caps the amount at long.MaxValue / 100 - about 292 years, far short of what a TimeSpan holds, but past any span these timestamps measure exactly: ElapsedTicks(SqlIntervalDifferenceExpression) above carries the same ceiling, and for the same reason.

TruncateDivide(ISqlExpression, long)

intDiv, because ClickHouse's / produces a float even between two integers.

protected override ISqlExpression TruncateDivide(ISqlExpression value, long divisor)

Parameters

value ISqlExpression
divisor long

Returns

ISqlExpression

WrapColumnExpression(ISqlExpression)

protected override ISqlExpression WrapColumnExpression(ISqlExpression expr)

Parameters

expr ISqlExpression

Returns

ISqlExpression