Table of Contents

Class IntervalLowering

Namespace
LinqToDB.Internal.SqlProvider
Assembly
linq2db.dll

Default lowering of interval nodes onto integral storage: the strategy used when a provider has no native interval type, or has one but the column is not mapped to it.

public static class IntervalLowering
Inheritance
object
IntervalLowering

Remarks

This is one strategy among several - native INTERVAL, TIME, OLE Automation dates - so it lives apart from the visitor that selects it. Every method returns null when the result cannot be produced exactly, which leaves the node untranslated rather than approximated.

Truncating division is taken as a parameter rather than emitted here, because the SQL for it varies by provider - see SqlExpressionConvertVisitor.TruncateDivide.

Methods

ElapsedTotal(ISqlExpressionFactory, SqlIntervalDifferenceExpression, SqlIntervalUnit, SqlIntervalUnit?, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?>, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?>)

The whole interval expressed in unit, fraction included - TotalHours and friends.

public static ISqlExpression? ElapsedTotal(ISqlExpressionFactory factory, SqlIntervalDifferenceExpression difference, SqlIntervalUnit unit, SqlIntervalUnit? finestUnit, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?> countBoundaries, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?> shiftDate)

Parameters

factory ISqlExpressionFactory
difference SqlIntervalDifferenceExpression
unit SqlIntervalUnit
finestUnit SqlIntervalUnit?
countBoundaries Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression>
shiftDate Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression>

Returns

ISqlExpression

Remarks

Built as whole units plus the leftover, never as one division of a tick count: a century in ticks exceeds long, so the undivided form is not representable at all. The leftover is measured between the anchor and the end, a window shorter than one unit, so the fine count is always small.

The raw boundary count, uncorrected, for the reason the elapsed tick count gives: the leftover is measured from wherever the anchor landed, so a count that overshot by one unit comes back as a leftover of the same size pointing the other way, and the two telescope. Correcting it first would change nothing about the answer and would put the correction - three comparisons and two arithmetic terms - into the expression twice over, once as the whole part and once inside the anchor.

A component is the other case and does need the correction, because there the whole number is the answer and nothing follows it to make up the difference.

ElapsedUnits(ISqlExpressionFactory, SqlIntervalDifferenceExpression, SqlIntervalUnit, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?>, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?>)

Elapsed whole units between two date/time values, reproducing _ticks / TicksPerUnit without ever materialising the tick count.

public static ISqlExpression? ElapsedUnits(ISqlExpressionFactory factory, SqlIntervalDifferenceExpression difference, SqlIntervalUnit unit, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?> countBoundaries, Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression?> shiftDate)

Parameters

factory ISqlExpressionFactory

Expression factory.

difference SqlIntervalDifferenceExpression

The difference whose elapsed units are wanted.

unit SqlIntervalUnit

Unit to count in.

countBoundaries Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression>

Provider's DATEDIFF - boundary counting.

shiftDate Func<SqlIntervalUnit, ISqlExpression, ISqlExpression, ISqlExpression>

Provider's DATEADD - must be exact.

Returns

ISqlExpression

Remarks

The provider's own difference counts crossed boundaries, so 10:59:30 to 11:01:00 reports two minutes where only one and a half elapsed. Shifting the start by that count and comparing against the end says whether it overshot, and the correction is at most one unit either way.

Counting whole units cannot overflow the way a fine-grained difference over the same range does - that limit is what caps DATEDIFF(millisecond, ...) at about 24 days.

FromTicks(ISqlExpressionFactory, ISqlExpression, SqlIntervalPartExpression, Func<ISqlExpression, long, ISqlExpression>, Func<ISqlExpression, long, ISqlExpression>)

public static ISqlExpression? FromTicks(ISqlExpressionFactory factory, ISqlExpression ticks, SqlIntervalPartExpression element, Func<ISqlExpression, long, ISqlExpression> truncateDivide, Func<ISqlExpression, long, ISqlExpression> truncateRemainder)

Parameters

factory ISqlExpressionFactory
ticks ISqlExpression
element SqlIntervalPartExpression
truncateDivide Func<ISqlExpression, long, ISqlExpression>
truncateRemainder Func<ISqlExpression, long, ISqlExpression>

Returns

ISqlExpression

Remarks

Where a provider can produce elapsed ticks directly, this answers every member from that one value, which is both shorter and closer to what .NET does than counting each unit separately. It is the second choice all the same: counting cannot overflow, and a fine-grained difference over a long range can.

LowerPart(ISqlExpressionFactory, SqlIntervalPartExpression, Func<ISqlExpression, long, ISqlExpression>, Func<ISqlExpression, long, ISqlExpression>)

Lowers a component or total of an interval into ordinary arithmetic.

public static ISqlExpression? LowerPart(ISqlExpressionFactory factory, SqlIntervalPartExpression element, Func<ISqlExpression, long, ISqlExpression> truncateDivide, Func<ISqlExpression, long, ISqlExpression> truncateRemainder)

Parameters

factory ISqlExpressionFactory

Expression factory.

element SqlIntervalPartExpression

Part to lower.

truncateDivide Func<ISqlExpression, long, ISqlExpression>

Integer division truncating toward zero, as the provider spells it.

truncateRemainder Func<ISqlExpression, long, ISqlExpression>

Remainder of that same division.

Returns

ISqlExpression

ToTicks(ISqlExpressionFactory, SqlIntervalExpression)

Converts an interval's stored amount to ticks, exactly.

public static ISqlExpression? ToTicks(ISqlExpressionFactory factory, SqlIntervalExpression interval)

Parameters

factory ISqlExpressionFactory
interval SqlIntervalExpression

Returns

ISqlExpression

WrapComponent(ISqlExpressionFactory, ISqlExpression, SqlIntervalUnit, SqlIntervalUnit?, Func<ISqlExpression, long, ISqlExpression>)

Wraps a whole-unit count into its CLR component, mirroring TimeSpan term for term: Hours is (_ticks / TicksPerHour) % 24, and this is the % 24.

public static ISqlExpression WrapComponent(ISqlExpressionFactory factory, ISqlExpression wholeUnits, SqlIntervalUnit unit, SqlIntervalUnit? within, Func<ISqlExpression, long, ISqlExpression> truncateRemainder)

Parameters

factory ISqlExpressionFactory
wholeUnits ISqlExpression
unit SqlIntervalUnit
within SqlIntervalUnit?
truncateRemainder Func<ISqlExpression, long, ISqlExpression>

Returns

ISqlExpression

Remarks

Days does not wrap - a duration may legitimately exceed a year, and .NET applies no modulo to it.