Table of Contents

Class AccessSqlExpressionConvertVisitor

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

Constructors

AccessSqlExpressionConvertVisitor(bool)

public AccessSqlExpressionConvertVisitor(bool allowModify)

Parameters

allowModify bool

Properties

CanLowerIntervalPart

Whether a member of an elapsed date difference can be lowered. Defaults to whatever the difference itself can do.

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

CanMeasureDifferenceInTicks

And no tick count either, for the reason ElapsedTicks(SqlIntervalDifferenceExpression) gives, which is what leaves a total below the resolution with nowhere to come from rather than merely quantised.

public override bool CanMeasureDifferenceInTicks { get; }

Property Value

bool

ElapsedTicksResolveMembers

Access counts seconds, and an OLE Automation date holds fractions of one, so a tick count derived from it is only good to the second - which is why the members are counted instead.

protected override bool ElapsedTicksResolveMembers { get; }

Property Value

bool

FinestDateUnit

A second is as fine as Access counts - DateDiff has no millisecond part at all.

protected override SqlIntervalUnit? FinestDateUnit { get; }

Property Value

SqlIntervalUnit?

Remarks

The whole-unit members are still exact, because the anchor correction compares actual dates rather than trusting the count. What this limits is the fraction of a Total and anything below a second, which an OLE Automation date - a floating day number - could not carry reliably anyway.

IntervalResolution

A second is also as fine as the measurement resolves, so a component asked for below one is identically zero rather than merely imprecise.

public override SqlIntervalUnit IntervalResolution { get; }

Property Value

SqlIntervalUnit

Remarks

Access refuses such a member either way - there is no millisecond date part to extract and no tick count to divide - but where it refuses decides what a caller can do about it. Declined here, while the expression is still being built, the message names the unit and the resolution, and a projection stays free to fall back to .NET, which holds both dates and answers exactly. Left to the builder, the refusal arrives as an exception with no such escape.

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[]

LikeIsEscapeSupported

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

public override bool LikeIsEscapeSupported { get; }

Property Value

bool

SupportsNullIf

protected override bool SupportsNullIf { get; }

Property Value

bool

Methods

ConvertCoalesce(SqlCoalesceExpression)

public override ISqlExpression ConvertCoalesce(SqlCoalesceExpression element)

Parameters

element SqlCoalesceExpression

Returns

ISqlExpression

ConvertConversion(SqlCastExpression)

Implements SqlCastExpression conversion.

protected override ISqlExpression ConvertConversion(SqlCastExpression cast)

Parameters

cast SqlCastExpression

Returns

ISqlExpression

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

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

CountDateBoundaries(SqlIntervalUnit, ISqlExpression, ISqlExpression)

Boundary counting through DateDiff, whose count is a 32-bit value - so a count in seconds is taken across the sub-day remainder rather than across the whole span.

protected override ISqlExpression? CountDateBoundaries(SqlIntervalUnit unit, ISqlExpression start, ISqlExpression end)

Parameters

unit SqlIntervalUnit
start ISqlExpression
end ISqlExpression

Returns

ISqlExpression

Remarks

A second count reaches the 32-bit limit after about sixty-eight years, which is inside the range a person's age can reach. Past it Access answers Numeric value out of range instead of a number, and it does so before any cast here can widen the result - the overflow happens while Access computes the count. Counting days first and seconds only from there keeps both counts small: days stay 32-bit for any date Access can hold, and the remainder spans at most a day. Every coarser datepart is counted directly, being far from the limit - minutes reach it only after four thousand years.

The two counts telescope, so the split is exact rather than an approximation. DateDiff truncates both operands to the unit and subtracts, which makes it additive through any intermediate point, and shifting by whole days leaves the time of day alone - so the day count contributes exactly its own seconds. That holds even when the day count overshoots the end, as it does between an evening and the following morning: the remainder comes back negative by the same amount.

CDbl is what keeps the product from overflowing in turn - Access multiplies in 32-bit integers and a century of days is past that once scaled to seconds. A cast to a wider integer would not do: Access has none to name, and a cast to a floating type renders as nothing here.

This provider is the only one that gets here - it is the only override of ElapsedTicksResolveMembers to false, and everywhere else a member is taken from the tick count instead.

ElapsedTicks(SqlIntervalDifferenceExpression)

No tick count from Access at all.

protected override ISqlExpression? ElapsedTicks(SqlIntervalDifferenceExpression element)

Parameters

element SqlIntervalDifferenceExpression

Returns

ISqlExpression

Remarks

DateDiff hands back a 32-bit count, and scaling seconds to ticks overflows it after about three and a half minutes - the driver answers Numeric value out of range. There is no wider integer to reach for, so the interval never becomes a value here and its member translator says so.

EscapeLikeCharacters(ISqlExpression, ref ISqlExpression?)

public override ISqlExpression EscapeLikeCharacters(ISqlExpression expression, ref ISqlExpression? escape)

Parameters

expression ISqlExpression
escape ISqlExpression

Returns

ISqlExpression

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 override string EscapeLikePattern(string str)

Parameters

str string

Raw pattern value.

Returns

string

Escaped pattern value.

LowerIntervalPart(SqlIntervalPartExpression)

Forms a component of an elapsed difference over the last day of it rather than over the whole span.

protected override ISqlExpression? LowerIntervalPart(SqlIntervalPartExpression element)

Parameters

element SqlIntervalPartExpression

Returns

ISqlExpression

Remarks

Everything this provider counts with is 32-bit, and a component asks for all three of it: the count, the amount the correction's anchor is shifted by, and MOD, which coerces both operands before dividing. A second count crosses that range after about sixty-eight years - inside a human lifetime - and the last of the three takes the elapsed count whole, so no split made inside the component can help.

Made outside it instead. A component wraps at a divisor of a day - twenty-four hours, sixty minutes, sixty seconds - so whole days contribute nothing to it, and the same answer comes from the part of the span that is left after them. That part is under a day, which every count and every shift here holds comfortably.

The day count has to be the elapsed one rather than the boundary one, which is the whole reason this is not simply DateDiff. A boundary count overshoots between an evening and the following morning, and an anchor past the end turns the remainder negative - a sign the modulo keeps, answering -20 where 40 was meant.

LowerTemporalArithmetic(SqlTemporalArithmeticExpression)

No shift by an interval either, for the reason ElapsedTicks(SqlIntervalDifferenceExpression) gives.

protected override ISqlExpression? LowerTemporalArithmetic(SqlTemporalArithmeticExpression element)

Parameters

element SqlTemporalArithmeticExpression

Returns

ISqlExpression

Remarks

The amount reaches a shift as a tick count whatever it was built from, and that is the one number Access cannot hold - scaling to ticks overflows its arithmetic and the driver answers Numeric value out of range. Refusing by name is the whole of the difference between this and a date that comes back wrong.

Access shifts dates perfectly well in seconds; what it cannot do is take delivery of the amount in ticks. Should a coarser hand-off ever exist, this is the override to drop.

ShiftDate(SqlIntervalUnit, ISqlExpression, ISqlExpression)

Shifts a date/time value by a whole number of units - the provider's DATEADD.

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

TruncateDivide(ISqlExpression, long)

Access has no FLOOR or CEILING, but its Fix is exactly truncation toward zero - the semantic the base composes those two functions to reach.

protected override ISqlExpression TruncateDivide(ISqlExpression value, long divisor)

Parameters

value ISqlExpression
divisor long

Returns

ISqlExpression

Remarks

Int would be wrong here: it rounds down, so Int(-2.5) is -3 where CLR integer division gives -2. Access division is floating, so no cast is needed before Fix.