@minnowdb/core 0.7.10 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/client.d.ts +3 -0
- package/dist/engine/client.js +8 -0
- package/dist/engine/database.js +189 -35
- package/dist/engine/errors.d.ts +2 -2
- package/dist/engine/errors.js +1 -1
- package/dist/engine/live-aggregate.d.ts +12 -0
- package/dist/engine/live-aggregate.js +226 -0
- package/dist/engine/live-patch.d.ts +21 -0
- package/dist/engine/live-patch.js +31 -0
- package/dist/engine/live.d.ts +12 -0
- package/dist/engine/live.js +52 -49
- package/dist/engine/optimizer.js +11 -6
- package/dist/engine/query-cache.js +2 -13
- package/dist/engine/query-generations.d.ts +8 -0
- package/dist/engine/query-generations.js +61 -0
- package/dist/engine/query-identity.d.ts +3 -0
- package/dist/engine/query-identity.js +41 -0
- package/dist/engine/query.d.ts +6 -11
- package/dist/engine/query.js +294 -428
- package/dist/engine/sql-domains.d.ts +4 -0
- package/dist/engine/sql-domains.js +121 -0
- package/dist/engine/sql-semantics.js +7 -4
- package/dist/engine/typed-live.js +28 -20
- package/dist/engine/windows.d.ts +12 -0
- package/dist/engine/windows.js +386 -0
- package/dist/plan/model.d.ts +1 -1
- package/package.json +1 -1
- package/postgres-feature-profile.json +6 -1
- package/sql-feature-matrix.json +20 -27
package/dist/engine/query.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeQueryIdentity } from "./query-identity.js";
|
|
1
2
|
import { civilFromDays, copyDate, dateIsoString, dateMilliseconds, daysFromCivil, epochDays, dateUtcDate, dateUtcFullYear, dateUtcMonth, setDateUtcDate, setDateUtcMonth } from "../date-value.js";
|
|
2
3
|
import { crossJoinPlan } from "../plan/model.js";
|
|
3
4
|
import { assertWellFormedString, wellFormedUtf8ByteLength } from "../block-format/unicode.js";
|
|
@@ -5,13 +6,14 @@ import { MAX_SQL_NESTING_DEPTH, MAX_SQL_PARAMETERS, MAX_SQL_SCALAR_RESULT_CHARAC
|
|
|
5
6
|
import { SqlCompileError } from "./errors.js";
|
|
6
7
|
import { cachedQueryTerms, ftsBm25Row, FtsStatsAccumulator, ftsMatchTruth, renderDocumentValue, tokenize as ftsTokenize, validateFtsQuery } from "./fts.js";
|
|
7
8
|
import { QueryMemoryContext } from "./memory.js";
|
|
8
|
-
import {
|
|
9
|
+
import { applyWindowFunctions } from "./windows.js";
|
|
10
|
+
import { applyWindowFunctions as applyWindowFunctions2 } from "./windows.js";
|
|
9
11
|
import { coerceComparisonOperands, coercedComparable, parseSqlTimestampText, stringArgument, readUntypedText } from "./sql-semantics.js";
|
|
10
12
|
import { simpleScalarFunctions } from "./sql-functions.js";
|
|
11
13
|
import { jsonArrowStep, jsonAtPath, jsonConstructor, jsonIsValid, parseJsonPath, jsonDocumentOf } from "./sql-json.js";
|
|
12
14
|
import { optimizePlan, rewriteBoundCalendarEqualities } from "./optimizer.js";
|
|
13
15
|
import { compareSqlValues as compareValues, compileLikePattern, compileSimilarPattern, encodeSqlEqualityValue, roundSqlNumber } from "./sql-semantics.js";
|
|
14
|
-
import { arrayDomainValue, boundedJsonText, collatedDomainValue, concatenatedSqlValue, dateDomainValue, exactNumericAsNumber, exactNumericBinary, exactNumericLiteral, decimalScaleOfNumber, exactNumericRounded, exactNumericUnary, exactNumericValue, externalSqlDomainColumnValue, externalSqlDomainValue, intervalDomainValue, isDateDomainValue, isExactNumeric, isSqlDomainValue, jsonDomainValue, normalizeSqlDomainValue, preservedJsonDomainValue, protectedSqlTextValue, timeDomainValue, uuidDomainValue } from "./sql-domains.js";
|
|
16
|
+
import { arrayDomainValue, boundedJsonText, collatedDomainValue, concatenatedSqlValue, dateDomainValue, exactNumericAsNumber, exactNumericBinary, exactNumericLiteral, decimalScaleOfNumber, exactNumericRounded, exactNumericUnary, exactNumericValue, externalSqlDomainColumnValue, externalSqlDomainValue, intervalDomainValue, temporalDomainPart, isDateDomainValue, isExactNumeric, isSqlDomainValue, jsonDomainValue, normalizeSqlDomainValue, preservedJsonDomainValue, protectedSqlTextValue, timeDomainValue, uuidDomainValue } from "./sql-domains.js";
|
|
15
17
|
import { columnarTableFromRows, prepareVectorQuery } from "./vector.js";
|
|
16
18
|
function unknownColumnDomains(columns) {
|
|
17
19
|
return columns.map(() => null);
|
|
@@ -64,6 +66,9 @@ const scalarFunctionNames = /* @__PURE__ */ new Set([
|
|
|
64
66
|
"TO_JSON",
|
|
65
67
|
"IS_JSON",
|
|
66
68
|
"ARRAY",
|
|
69
|
+
"MINNOW_ARRAY_AT",
|
|
70
|
+
"MINNOW_ARRAY_FROM_JSON",
|
|
71
|
+
"MINNOW_ARRAY_ELEMENT",
|
|
67
72
|
"MINNOW_JSON_GET",
|
|
68
73
|
"MINNOW_JSON_GET_TEXT",
|
|
69
74
|
"MINNOW_TUPLE_KEY",
|
|
@@ -90,6 +95,7 @@ const functionSpellings = /* @__PURE__ */ new Map([
|
|
|
90
95
|
["CHAR_LENGTH", "LENGTH"],
|
|
91
96
|
["CHARACTER_LENGTH", "LENGTH"],
|
|
92
97
|
["POW", "POWER"],
|
|
98
|
+
["ARRAY_AGG", "JSON_ARRAYAGG"],
|
|
93
99
|
["JSON_AGG", "JSON_ARRAYAGG"],
|
|
94
100
|
["JSONB_AGG", "JSON_ARRAYAGG"],
|
|
95
101
|
["JSON_BUILD_ARRAY", "JSON_ARRAY"],
|
|
@@ -159,6 +165,12 @@ function castValue(value, target) {
|
|
|
159
165
|
if (value instanceof Date)
|
|
160
166
|
return protectedSqlTextValue(dateIsoString(value));
|
|
161
167
|
}
|
|
168
|
+
if (target === "number-integer" && isExactNumeric(value)) {
|
|
169
|
+
const integer = Number(externalSqlDomainValue(exactNumericRounded(value, 0, "round")));
|
|
170
|
+
if (!Number.isSafeInteger(integer))
|
|
171
|
+
throw new RangeError("Integer cast is outside the exact safe range");
|
|
172
|
+
return integer;
|
|
173
|
+
}
|
|
162
174
|
if (target === "number" || target === "number-integer") {
|
|
163
175
|
const external = externalSqlDomainValue(value);
|
|
164
176
|
let parsed;
|
|
@@ -168,6 +180,9 @@ function castValue(value, target) {
|
|
|
168
180
|
parsed = external ? 1 : 0;
|
|
169
181
|
else if (typeof external === "string") {
|
|
170
182
|
const text = external.trim();
|
|
183
|
+
if (target === "number-integer" && !/^[+-]?\d+$/.test(text)) {
|
|
184
|
+
throw new TypeError(`Cannot cast this string to a number: ${text} (expected integer text)`);
|
|
185
|
+
}
|
|
171
186
|
const candidate = text === "" ? Number.NaN : Number(text);
|
|
172
187
|
if (!Number.isFinite(candidate)) {
|
|
173
188
|
throw new TypeError(`Cannot cast this string to a number: ${text}`);
|
|
@@ -197,10 +212,9 @@ function castValue(value, target) {
|
|
|
197
212
|
if (typeof value === "string") {
|
|
198
213
|
const external = externalSqlDomainValue(value);
|
|
199
214
|
const text = typeof external === "string" ? external.trim().toLowerCase() : "";
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return false;
|
|
215
|
+
const parsed = readUntypedText("boolean", text);
|
|
216
|
+
if (typeof parsed === "boolean")
|
|
217
|
+
return parsed;
|
|
204
218
|
throw new TypeError(`Cannot cast this string to a boolean: ${typeof external === "string" ? external : value}`);
|
|
205
219
|
}
|
|
206
220
|
}
|
|
@@ -344,6 +358,32 @@ function scalarFunctionValueGeneric(name, values) {
|
|
|
344
358
|
}
|
|
345
359
|
if (name === "ARRAY")
|
|
346
360
|
return arrayDomainValue(values);
|
|
361
|
+
if (name === "MINNOW_ARRAY_ELEMENT") {
|
|
362
|
+
const value = externalSqlDomainValue(values[0]);
|
|
363
|
+
return typeof value === "string" ? protectedSqlTextValue(value) : value;
|
|
364
|
+
}
|
|
365
|
+
if (name === "MINNOW_ARRAY_FROM_JSON") {
|
|
366
|
+
if (values[0] === null)
|
|
367
|
+
return null;
|
|
368
|
+
return normalizeSqlDomainValue({ kind: "array", element: "TEXT" }, externalSqlDomainValue(values[0]));
|
|
369
|
+
}
|
|
370
|
+
if (name === "MINNOW_ARRAY_AT") {
|
|
371
|
+
if (values[0] === null || values[1] === null)
|
|
372
|
+
return null;
|
|
373
|
+
const source = externalSqlDomainValue(values[0]);
|
|
374
|
+
const index = values[1];
|
|
375
|
+
if (typeof source !== "string" || typeof index !== "number" || !Number.isSafeInteger(index))
|
|
376
|
+
throw new TypeError("Array subscripts require an array and an integer position");
|
|
377
|
+
const array = JSON.parse(source);
|
|
378
|
+
if (!Array.isArray(array))
|
|
379
|
+
throw new TypeError("Array subscripts require an array");
|
|
380
|
+
const selected = array[index - 1] ?? null;
|
|
381
|
+
if (typeof selected === "string")
|
|
382
|
+
return protectedSqlTextValue(selected);
|
|
383
|
+
if (selected === null || typeof selected === "number" || typeof selected === "boolean")
|
|
384
|
+
return selected;
|
|
385
|
+
throw new TypeError("Array subscripts currently require one-dimensional scalar arrays");
|
|
386
|
+
}
|
|
347
387
|
if (name === "MINNOW_COLLATE")
|
|
348
388
|
return collatedDomainValue(values[0], values[1]);
|
|
349
389
|
if (name === "NEXTVAL" || name === "CURRVAL") {
|
|
@@ -720,7 +760,7 @@ function dateAddValue(value, months, milliseconds) {
|
|
|
720
760
|
setDateUtcDate(shifted, Math.min(day, dateUtcDate(lastDay)));
|
|
721
761
|
}
|
|
722
762
|
const result = new Date(dateMilliseconds(shifted) + millisecondCount);
|
|
723
|
-
return
|
|
763
|
+
return result;
|
|
724
764
|
}
|
|
725
765
|
function dateTruncValue(unit, value) {
|
|
726
766
|
if (typeof unit !== "string" || !dateTruncUnits.has(unit.toLowerCase())) {
|
|
@@ -1902,6 +1942,7 @@ function inferBlockSchema(plan, schemas) {
|
|
|
1902
1942
|
const wildcardSchema = (source) => (schemas.get(source.table) ?? []).filter((column) => !column.name.startsWith("\0")).map((column) => ({
|
|
1903
1943
|
name: multipleSources ? `${source.alias}.${column.name}` : column.name,
|
|
1904
1944
|
type: column.type,
|
|
1945
|
+
...column.unknown === true ? { unknown: true } : {},
|
|
1905
1946
|
...column.integer === true ? { integer: true } : {},
|
|
1906
1947
|
...column.sqlDomain === void 0 ? {} : { sqlDomain: column.sqlDomain }
|
|
1907
1948
|
}));
|
|
@@ -1916,9 +1957,9 @@ function inferBlockSchema(plan, schemas) {
|
|
|
1916
1957
|
const column = (schemas.get(source.table) ?? []).find(({ name }) => name === parts[1]);
|
|
1917
1958
|
if (column === void 0)
|
|
1918
1959
|
throw new TypeError(`Unknown column: ${reference}`);
|
|
1919
|
-
return column.type;
|
|
1960
|
+
return column.unknown === true ? "null" : column.type;
|
|
1920
1961
|
}
|
|
1921
|
-
const matches = sources.flatMap((source) => (schemas.get(source.table) ?? []).filter(({ name }) => name === parts[0]).map((column) => column.type));
|
|
1962
|
+
const matches = sources.flatMap((source) => (schemas.get(source.table) ?? []).filter(({ name }) => name === parts[0]).map((column) => column.unknown === true ? "null" : column.type));
|
|
1922
1963
|
if (matches.length !== 1)
|
|
1923
1964
|
throw new TypeError(`Ambiguous or missing column: ${reference}`);
|
|
1924
1965
|
return matches[0] ?? "string";
|
|
@@ -2007,6 +2048,16 @@ function inferBlockSchema(plan, schemas) {
|
|
|
2007
2048
|
return void 0;
|
|
2008
2049
|
if (expression.name === "CURRENT_DATE")
|
|
2009
2050
|
return { kind: "date" };
|
|
2051
|
+
if (expression.name === "ARRAY" || expression.name === "MINNOW_ARRAY_FROM_JSON") {
|
|
2052
|
+
let argument = expression.name === "ARRAY" ? expression.arguments[0] : expression.arguments[0]?.kind === "call" ? expression.arguments[0].arguments[0] : void 0;
|
|
2053
|
+
if (argument?.kind === "call" && argument.name === "MINNOW_ARRAY_ELEMENT")
|
|
2054
|
+
argument = argument.arguments[0];
|
|
2055
|
+
const type = argument === void 0 ? "string" : infer(argument);
|
|
2056
|
+
return {
|
|
2057
|
+
kind: "array",
|
|
2058
|
+
element: type === "number" ? "DOUBLE" : type === "boolean" ? "BOOLEAN" : type === "datetime" ? "TIMESTAMP" : "TEXT"
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2010
2061
|
const simple = simpleScalarFunctions.get(expression.name);
|
|
2011
2062
|
if (simple?.returns === "date")
|
|
2012
2063
|
return { kind: "date" };
|
|
@@ -2040,11 +2091,8 @@ function inferBlockSchema(plan, schemas) {
|
|
|
2040
2091
|
const sides = expression.arguments.map((argument) => [argument, inferDomain(argument)]);
|
|
2041
2092
|
return sides.some(([, domain]) => domain?.kind === "numeric") ? numericResultDomain("%", sides) : void 0;
|
|
2042
2093
|
}
|
|
2043
|
-
if (expression.name === "DATE_ADD")
|
|
2044
|
-
|
|
2045
|
-
const milliseconds = expression.arguments[2];
|
|
2046
|
-
return input?.kind === "date" && milliseconds?.kind === "literal" && typeof milliseconds.value === "number" && milliseconds.value % 864e5 === 0 ? input : void 0;
|
|
2047
|
-
}
|
|
2094
|
+
if (expression.name === "DATE_ADD")
|
|
2095
|
+
return void 0;
|
|
2048
2096
|
if (expression.name === "SUM" || expression.name === "AVG" || expression.name === "MIN" || expression.name === "MAX" || expression.name === "MINNOW_SINGLE_VALUE" || expression.name === "COALESCE" || expression.name === "NULLIF" || expression.name === "GREATEST" || expression.name === "LEAST") {
|
|
2049
2097
|
return expression.arguments.map(inferDomain).find((domain) => domain !== void 0);
|
|
2050
2098
|
}
|
|
@@ -2143,7 +2191,12 @@ function inferBlockSchema(plan, schemas) {
|
|
|
2143
2191
|
if (expression.name === "LENGTH" || expression.name === "POWER" || expression.name === "SQRT" || expression.name === "INSTR" || expression.name === "EXTRACT" || expression.name === "OCTET_LENGTH" || expression.name === "GROUPING" || expression.name === "NEXTVAL" || expression.name === "CURRVAL" || expression.name === "RANDOM") {
|
|
2144
2192
|
return "number";
|
|
2145
2193
|
}
|
|
2146
|
-
if (expression.name === "
|
|
2194
|
+
if (expression.name === "MINNOW_ARRAY_AT") {
|
|
2195
|
+
const argument2 = expression.arguments[0];
|
|
2196
|
+
const domain = argument2 === void 0 ? void 0 : inferDomain(argument2);
|
|
2197
|
+
return domain?.kind === "array" ? createTableTypeNames.get(domain.element.toUpperCase()) ?? "string" : "string";
|
|
2198
|
+
}
|
|
2199
|
+
if (expression.name === "MINNOW_ARRAY_FROM_JSON" || expression.name === "JSON_ARRAYAGG" || expression.name === "STRING_AGG" || expression.name === "JSON_VALUE" || expression.name === "JSON_QUERY" || expression.name === "JSON_OBJECT" || expression.name === "JSON_ARRAY" || expression.name === "TO_JSON" || expression.name === "ARRAY" || expression.name === "MINNOW_JSON_GET" || expression.name === "MINNOW_JSON_GET_TEXT" || expression.name === "MINNOW_TUPLE_KEY" || expression.name === "MINNOW_COLLATE" || expression.name === "GEN_RANDOM_UUID") {
|
|
2147
2200
|
return "string";
|
|
2148
2201
|
}
|
|
2149
2202
|
if (expression.name === "JSON_EXISTS" || expression.name === "IS_JSON")
|
|
@@ -2161,9 +2214,8 @@ function inferBlockSchema(plan, schemas) {
|
|
|
2161
2214
|
}
|
|
2162
2215
|
if (expression.name === "DATE_TRUNC")
|
|
2163
2216
|
return "datetime";
|
|
2164
|
-
if (expression.name === "DATE_ADD")
|
|
2165
|
-
return
|
|
2166
|
-
}
|
|
2217
|
+
if (expression.name === "DATE_ADD")
|
|
2218
|
+
return "datetime";
|
|
2167
2219
|
if (expression.name === "CURRENT_DATE")
|
|
2168
2220
|
return "string";
|
|
2169
2221
|
if (expression.name === "CURRENT_TIMESTAMP")
|
|
@@ -2238,9 +2290,8 @@ function inferBlockSchema(plan, schemas) {
|
|
|
2238
2290
|
return wildcardSchema(source);
|
|
2239
2291
|
}
|
|
2240
2292
|
const type = infer(item.expression);
|
|
2241
|
-
if (type === "null")
|
|
2242
|
-
|
|
2243
|
-
}
|
|
2293
|
+
if (type === "null")
|
|
2294
|
+
return [{ name: item.alias, type: "string", unknown: true }];
|
|
2244
2295
|
const integer = integerTypedExpression(item.expression, resolveColumnInteger);
|
|
2245
2296
|
const sqlDomain = inferDomain(item.expression);
|
|
2246
2297
|
return [
|
|
@@ -2974,22 +3025,29 @@ function planMayHaveIntegerDivision(plan) {
|
|
|
2974
3025
|
return state.found;
|
|
2975
3026
|
}
|
|
2976
3027
|
function foldIdentifierCase(plan, tables) {
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
bucket.
|
|
2985
|
-
|
|
3028
|
+
let lowerTables;
|
|
3029
|
+
const caseInsensitiveTables = () => {
|
|
3030
|
+
if (lowerTables !== void 0)
|
|
3031
|
+
return lowerTables;
|
|
3032
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
3033
|
+
for (const name of tables.keys()) {
|
|
3034
|
+
const lowered = name.toLowerCase();
|
|
3035
|
+
const bucket = indexed.get(lowered);
|
|
3036
|
+
if (bucket === void 0)
|
|
3037
|
+
indexed.set(lowered, [name]);
|
|
3038
|
+
else
|
|
3039
|
+
bucket.push(name);
|
|
3040
|
+
}
|
|
3041
|
+
lowerTables = indexed;
|
|
3042
|
+
return indexed;
|
|
3043
|
+
};
|
|
2986
3044
|
const foldTable = (name) => {
|
|
2987
3045
|
if (tables.has(name))
|
|
2988
3046
|
return void 0;
|
|
2989
3047
|
const lowered = name.toLowerCase();
|
|
2990
3048
|
if (tables.has(lowered))
|
|
2991
3049
|
return lowered;
|
|
2992
|
-
const candidates =
|
|
3050
|
+
const candidates = caseInsensitiveTables().get(lowered);
|
|
2993
3051
|
return candidates?.length === 1 ? candidates[0] : void 0;
|
|
2994
3052
|
};
|
|
2995
3053
|
const foldColumn = (columns, name) => {
|
|
@@ -3127,16 +3185,7 @@ function expandRowReferences(plan, tableColumns) {
|
|
|
3127
3185
|
const sourceColumns = (source) => {
|
|
3128
3186
|
if (source.columnAliases !== void 0)
|
|
3129
3187
|
return source.columnAliases;
|
|
3130
|
-
|
|
3131
|
-
return source.derived.select.map((item) => item.alias);
|
|
3132
|
-
if (source.union !== void 0) {
|
|
3133
|
-
return source.union.blocks[0]?.select.map((item) => item.alias) ?? [];
|
|
3134
|
-
}
|
|
3135
|
-
if (source.windowed !== void 0)
|
|
3136
|
-
return source.windowed.block.select.map((item) => item.alias);
|
|
3137
|
-
if (source.recursive !== void 0)
|
|
3138
|
-
return [];
|
|
3139
|
-
return tableColumns(source.table) ?? [];
|
|
3188
|
+
return sourceWildcardColumns(source, tableColumns) ?? [];
|
|
3140
3189
|
};
|
|
3141
3190
|
const rewriteBlock = (block) => {
|
|
3142
3191
|
if (pass.probing && pass.needed)
|
|
@@ -3483,6 +3532,26 @@ function exactConstantFold(expression) {
|
|
|
3483
3532
|
return folded === void 0 ? void 0 : { value: folded, seeded };
|
|
3484
3533
|
}
|
|
3485
3534
|
function resolveExactNumericConstants(expression) {
|
|
3535
|
+
if (expression.kind === "call" && expression.name === "CAST") {
|
|
3536
|
+
const [source, target] = expression.arguments;
|
|
3537
|
+
if (source !== void 0 && target?.kind === "literal" && target.value === "number-integer") {
|
|
3538
|
+
const exact = exactConstantFold(source);
|
|
3539
|
+
if (exact?.seeded === true && exact.value !== null) {
|
|
3540
|
+
return {
|
|
3541
|
+
...expression,
|
|
3542
|
+
arguments: [
|
|
3543
|
+
{
|
|
3544
|
+
kind: "literal",
|
|
3545
|
+
value: exactNumericValue(exact.value),
|
|
3546
|
+
internalSqlValue: true,
|
|
3547
|
+
sqlDomain: { kind: "numeric" }
|
|
3548
|
+
},
|
|
3549
|
+
target
|
|
3550
|
+
]
|
|
3551
|
+
};
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3486
3555
|
let folded;
|
|
3487
3556
|
try {
|
|
3488
3557
|
folded = exactConstantFold(expression);
|
|
@@ -3578,296 +3647,6 @@ function resolveStatementExactNumericConstants(statement) {
|
|
|
3578
3647
|
}
|
|
3579
3648
|
}
|
|
3580
3649
|
}
|
|
3581
|
-
function aggregateWindowMembers(window, values, members, avgScale) {
|
|
3582
|
-
const present = members.filter((member) => {
|
|
3583
|
-
const value = values[member];
|
|
3584
|
-
return value !== null && value !== void 0;
|
|
3585
|
-
});
|
|
3586
|
-
if (window.name === "COUNT") {
|
|
3587
|
-
return window.argumentAlias === void 0 ? members.length : present.length;
|
|
3588
|
-
}
|
|
3589
|
-
if (window.name === "FIRST_VALUE")
|
|
3590
|
-
return values[members[0] ?? -1] ?? null;
|
|
3591
|
-
if (window.name === "LAST_VALUE")
|
|
3592
|
-
return values[members[members.length - 1] ?? -1] ?? null;
|
|
3593
|
-
if (window.name === "NTH_VALUE")
|
|
3594
|
-
return values[members[(window.offset ?? 1) - 1] ?? -1] ?? null;
|
|
3595
|
-
if (present.length === 0)
|
|
3596
|
-
return null;
|
|
3597
|
-
if (window.name === "SUM" || window.name === "AVG") {
|
|
3598
|
-
const exact = present.some((member) => isExactNumeric(values[member]));
|
|
3599
|
-
if (exact) {
|
|
3600
|
-
let total2 = exactNumericValue(0);
|
|
3601
|
-
for (const member of present) {
|
|
3602
|
-
const value = values[member];
|
|
3603
|
-
if (!isExactNumeric(value)) {
|
|
3604
|
-
throw new TypeError("Exact NUMERIC window input mixed with an approximate number");
|
|
3605
|
-
}
|
|
3606
|
-
const next = exactNumericBinary("+", total2, value);
|
|
3607
|
-
if (next === null || next === void 0)
|
|
3608
|
-
throw new Error("Exact NUMERIC sum disappeared");
|
|
3609
|
-
total2 = next;
|
|
3610
|
-
}
|
|
3611
|
-
return window.name === "SUM" ? total2 : exactNumericBinary("/", total2, present.length, avgScale);
|
|
3612
|
-
}
|
|
3613
|
-
const total = present.reduce((sum, member) => sum + numeric(values[member]), 0);
|
|
3614
|
-
return window.name === "SUM" ? total : total / present.length;
|
|
3615
|
-
}
|
|
3616
|
-
let best;
|
|
3617
|
-
for (const member of present) {
|
|
3618
|
-
const candidate = values[member];
|
|
3619
|
-
if (best === void 0 || (window.name === "MIN" ? compareValues(candidate, best) < 0 : compareValues(candidate, best) > 0)) {
|
|
3620
|
-
best = candidate;
|
|
3621
|
-
}
|
|
3622
|
-
}
|
|
3623
|
-
return best ?? null;
|
|
3624
|
-
}
|
|
3625
|
-
function applyAggregateWindow(rows, indexes, window, samePartition, sameOrderKeys, avgScale) {
|
|
3626
|
-
const frame = window.frame ?? {
|
|
3627
|
-
unit: "range",
|
|
3628
|
-
start: { kind: "unbounded-preceding" },
|
|
3629
|
-
end: window.orderAliases.length === 0 ? { kind: "unbounded-following" } : { kind: "current-row" }
|
|
3630
|
-
};
|
|
3631
|
-
let start = 0;
|
|
3632
|
-
while (start < indexes.length) {
|
|
3633
|
-
let end = start + 1;
|
|
3634
|
-
while (end < indexes.length && samePartition(indexes[start] ?? 0, indexes[end] ?? 0)) {
|
|
3635
|
-
end += 1;
|
|
3636
|
-
}
|
|
3637
|
-
applyAggregateWindowPartition(rows, indexes, window, frame, sameOrderKeys, start, end, avgScale);
|
|
3638
|
-
start = end;
|
|
3639
|
-
}
|
|
3640
|
-
}
|
|
3641
|
-
function applyAggregateWindowPartition(rows, indexes, window, frame, sameOrderKeys, start, end, avgScale) {
|
|
3642
|
-
const size = end - start;
|
|
3643
|
-
const peerStart = new Array(size).fill(0);
|
|
3644
|
-
const peerEnd = new Array(size).fill(size);
|
|
3645
|
-
if (window.orderAliases.length > 0) {
|
|
3646
|
-
let groupBegin = 0;
|
|
3647
|
-
for (let position = 1; position <= size; position += 1) {
|
|
3648
|
-
if (position === size || !sameOrderKeys(indexes[start + groupBegin] ?? 0, indexes[start + position] ?? 0)) {
|
|
3649
|
-
for (let member = groupBegin; member < position; member += 1) {
|
|
3650
|
-
peerStart[member] = groupBegin;
|
|
3651
|
-
peerEnd[member] = position;
|
|
3652
|
-
}
|
|
3653
|
-
groupBegin = position;
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
}
|
|
3657
|
-
const values = [];
|
|
3658
|
-
const sums = window.name === "SUM" || window.name === "AVG";
|
|
3659
|
-
for (let position = 0; position < size; position += 1) {
|
|
3660
|
-
const value = window.argumentAlias === void 0 ? void 0 : rows[indexes[start + position] ?? -1]?.[window.argumentAlias] ?? null;
|
|
3661
|
-
values.push(value);
|
|
3662
|
-
}
|
|
3663
|
-
const exactSums = sums && values.some((value) => isExactNumeric(value));
|
|
3664
|
-
const prefixNonNull = new Float64Array(size + 1);
|
|
3665
|
-
const prefixSum = exactSums ? void 0 : new Float64Array(size + 1);
|
|
3666
|
-
const prefixExact = exactSums ? new Array(size + 1) : void 0;
|
|
3667
|
-
const exactZero = exactNumericValue(0);
|
|
3668
|
-
if (exactZero === null)
|
|
3669
|
-
throw new Error("Exact NUMERIC zero disappeared");
|
|
3670
|
-
if (prefixExact !== void 0)
|
|
3671
|
-
prefixExact[0] = exactZero;
|
|
3672
|
-
for (let position = 0; position < size; position += 1) {
|
|
3673
|
-
const value = values[position];
|
|
3674
|
-
const nonNull = window.argumentAlias !== void 0 && value !== null && value !== void 0;
|
|
3675
|
-
prefixNonNull[position + 1] = (prefixNonNull[position] ?? 0) + (nonNull ? 1 : 0);
|
|
3676
|
-
if (prefixExact !== void 0) {
|
|
3677
|
-
if (nonNull && !isExactNumeric(value)) {
|
|
3678
|
-
throw new TypeError("Exact NUMERIC window input mixed with an approximate number");
|
|
3679
|
-
}
|
|
3680
|
-
const previous = prefixExact[position] ?? exactZero;
|
|
3681
|
-
const next = nonNull ? exactNumericBinary("+", previous, value) : previous;
|
|
3682
|
-
if (next === null || next === void 0)
|
|
3683
|
-
throw new Error("Exact NUMERIC sum disappeared");
|
|
3684
|
-
prefixExact[position + 1] = next;
|
|
3685
|
-
} else if (prefixSum !== void 0) {
|
|
3686
|
-
prefixSum[position + 1] = (prefixSum[position] ?? 0) + (sums && nonNull ? numeric(value) : 0);
|
|
3687
|
-
}
|
|
3688
|
-
}
|
|
3689
|
-
const groupOrdinal = new Array(size).fill(0);
|
|
3690
|
-
const groupStarts = [];
|
|
3691
|
-
if (frame.unit === "groups") {
|
|
3692
|
-
for (let position = 0; position < size; position += 1) {
|
|
3693
|
-
if (position === 0 || peerStart[position] !== peerStart[position - 1]) {
|
|
3694
|
-
groupStarts.push(peerStart[position] ?? position);
|
|
3695
|
-
}
|
|
3696
|
-
groupOrdinal[position] = groupStarts.length - 1;
|
|
3697
|
-
}
|
|
3698
|
-
}
|
|
3699
|
-
const groupEdge = (ordinal, isStart) => {
|
|
3700
|
-
if (ordinal < 0)
|
|
3701
|
-
return isStart ? 0 : 0;
|
|
3702
|
-
if (ordinal >= groupStarts.length)
|
|
3703
|
-
return size;
|
|
3704
|
-
return isStart ? groupStarts[ordinal] ?? 0 : groupStarts[ordinal + 1] ?? size;
|
|
3705
|
-
};
|
|
3706
|
-
const bound = (edge, position, isStart) => {
|
|
3707
|
-
switch (edge.kind) {
|
|
3708
|
-
case "unbounded-preceding":
|
|
3709
|
-
return 0;
|
|
3710
|
-
case "unbounded-following":
|
|
3711
|
-
return size;
|
|
3712
|
-
case "preceding":
|
|
3713
|
-
if (frame.unit === "groups") {
|
|
3714
|
-
return groupEdge((groupOrdinal[position] ?? 0) - (edge.offset ?? 0), isStart);
|
|
3715
|
-
}
|
|
3716
|
-
return position - (edge.offset ?? 0) + (isStart ? 0 : 1);
|
|
3717
|
-
case "following":
|
|
3718
|
-
if (frame.unit === "groups") {
|
|
3719
|
-
return groupEdge((groupOrdinal[position] ?? 0) + (edge.offset ?? 0), isStart);
|
|
3720
|
-
}
|
|
3721
|
-
return position + (edge.offset ?? 0) + (isStart ? 0 : 1);
|
|
3722
|
-
case "current-row":
|
|
3723
|
-
if (frame.unit === "range" || frame.unit === "groups") {
|
|
3724
|
-
return (isStart ? peerStart[position] : peerEnd[position]) ?? position;
|
|
3725
|
-
}
|
|
3726
|
-
return position + (isStart ? 0 : 1);
|
|
3727
|
-
}
|
|
3728
|
-
};
|
|
3729
|
-
const excluded = (position) => {
|
|
3730
|
-
switch (frame.exclude) {
|
|
3731
|
-
case "current-row":
|
|
3732
|
-
return { from: position, to: position + 1, keepCurrent: false };
|
|
3733
|
-
case "group":
|
|
3734
|
-
return {
|
|
3735
|
-
from: peerStart[position] ?? position,
|
|
3736
|
-
to: peerEnd[position] ?? position + 1,
|
|
3737
|
-
keepCurrent: false
|
|
3738
|
-
};
|
|
3739
|
-
case "ties":
|
|
3740
|
-
return {
|
|
3741
|
-
from: peerStart[position] ?? position,
|
|
3742
|
-
to: peerEnd[position] ?? position + 1,
|
|
3743
|
-
keepCurrent: true
|
|
3744
|
-
};
|
|
3745
|
-
default:
|
|
3746
|
-
return { from: 0, to: 0, keepCurrent: true };
|
|
3747
|
-
}
|
|
3748
|
-
};
|
|
3749
|
-
for (let position = 0; position < size; position += 1) {
|
|
3750
|
-
const low = Math.max(0, Math.min(size, bound(frame.start, position, true)));
|
|
3751
|
-
const high = Math.max(0, Math.min(size, bound(frame.end, position, false)));
|
|
3752
|
-
let value;
|
|
3753
|
-
if (frame.exclude !== void 0) {
|
|
3754
|
-
const skip = excluded(position);
|
|
3755
|
-
const members = [];
|
|
3756
|
-
for (let member = low; member < high; member += 1) {
|
|
3757
|
-
const dropped = member >= skip.from && member < skip.to && !(skip.keepCurrent && member === position);
|
|
3758
|
-
if (!dropped)
|
|
3759
|
-
members.push(member);
|
|
3760
|
-
}
|
|
3761
|
-
value = aggregateWindowMembers(window, values, members, avgScale);
|
|
3762
|
-
const row2 = rows[indexes[start + position] ?? -1];
|
|
3763
|
-
if (row2 !== void 0)
|
|
3764
|
-
row2[window.alias] = asQueryValue(value);
|
|
3765
|
-
continue;
|
|
3766
|
-
}
|
|
3767
|
-
if (high <= low) {
|
|
3768
|
-
value = window.name === "COUNT" ? 0 : null;
|
|
3769
|
-
} else if (window.name === "FIRST_VALUE") {
|
|
3770
|
-
value = values[low] ?? null;
|
|
3771
|
-
} else if (window.name === "NTH_VALUE") {
|
|
3772
|
-
const position2 = low + (window.offset ?? 1) - 1;
|
|
3773
|
-
value = position2 < high ? values[position2] ?? null : null;
|
|
3774
|
-
} else if (window.name === "LAST_VALUE") {
|
|
3775
|
-
value = values[high - 1] ?? null;
|
|
3776
|
-
} else if (window.name === "COUNT") {
|
|
3777
|
-
value = window.argumentAlias === void 0 ? high - low : (prefixNonNull[high] ?? 0) - (prefixNonNull[low] ?? 0);
|
|
3778
|
-
} else if (sums) {
|
|
3779
|
-
const nonNull = (prefixNonNull[high] ?? 0) - (prefixNonNull[low] ?? 0);
|
|
3780
|
-
if (nonNull === 0) {
|
|
3781
|
-
value = null;
|
|
3782
|
-
} else if (prefixExact !== void 0) {
|
|
3783
|
-
const total = exactNumericBinary("-", prefixExact[high] ?? exactZero, prefixExact[low] ?? exactZero);
|
|
3784
|
-
value = window.name === "SUM" ? total : exactNumericBinary("/", total, nonNull, avgScale);
|
|
3785
|
-
} else {
|
|
3786
|
-
const total = (prefixSum?.[high] ?? 0) - (prefixSum?.[low] ?? 0);
|
|
3787
|
-
value = window.name === "SUM" ? total : total / nonNull;
|
|
3788
|
-
}
|
|
3789
|
-
} else {
|
|
3790
|
-
let best;
|
|
3791
|
-
for (let member = low; member < high; member += 1) {
|
|
3792
|
-
const candidate = values[member];
|
|
3793
|
-
if (candidate === null || candidate === void 0)
|
|
3794
|
-
continue;
|
|
3795
|
-
if (best === void 0 || (window.name === "MIN" ? compareValues(candidate, best) < 0 : compareValues(candidate, best) > 0)) {
|
|
3796
|
-
best = candidate;
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
value = best ?? null;
|
|
3800
|
-
}
|
|
3801
|
-
const row = rows[indexes[start + position] ?? -1];
|
|
3802
|
-
if (row !== void 0)
|
|
3803
|
-
row[window.alias] = asQueryValue(value);
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
function applyDistributionWindow(rows, indexes, window, samePartition, sameOrderKeys) {
|
|
3807
|
-
let start = 0;
|
|
3808
|
-
while (start < indexes.length) {
|
|
3809
|
-
let end = start + 1;
|
|
3810
|
-
while (end < indexes.length && samePartition(indexes[start] ?? 0, indexes[end] ?? 0)) {
|
|
3811
|
-
end += 1;
|
|
3812
|
-
}
|
|
3813
|
-
const size = end - start;
|
|
3814
|
-
let groupBegin = 0;
|
|
3815
|
-
const assign = (position, value) => {
|
|
3816
|
-
const row = rows[indexes[start + position] ?? -1];
|
|
3817
|
-
if (row !== void 0)
|
|
3818
|
-
row[window.alias] = value;
|
|
3819
|
-
};
|
|
3820
|
-
for (let position = 1; position <= size; position += 1) {
|
|
3821
|
-
if (position === size || !sameOrderKeys(indexes[start + groupBegin] ?? 0, indexes[start + position] ?? 0)) {
|
|
3822
|
-
for (let member = groupBegin; member < position; member += 1) {
|
|
3823
|
-
if (window.name === "PERCENT_RANK") {
|
|
3824
|
-
assign(member, size === 1 ? 0 : groupBegin / (size - 1));
|
|
3825
|
-
} else if (window.name === "CUME_DIST") {
|
|
3826
|
-
assign(member, position / size);
|
|
3827
|
-
}
|
|
3828
|
-
}
|
|
3829
|
-
groupBegin = position;
|
|
3830
|
-
}
|
|
3831
|
-
}
|
|
3832
|
-
if (window.name === "NTILE") {
|
|
3833
|
-
const buckets = window.offset ?? 1;
|
|
3834
|
-
const bucketSize = Math.floor(size / buckets);
|
|
3835
|
-
const remainder = size % buckets;
|
|
3836
|
-
let position = 0;
|
|
3837
|
-
for (let bucket = 1; bucket <= buckets && position < size; bucket += 1) {
|
|
3838
|
-
const width = bucketSize + (bucket <= remainder ? 1 : 0);
|
|
3839
|
-
for (let member = 0; member < width && position < size; member += 1, position += 1) {
|
|
3840
|
-
assign(position, bucket);
|
|
3841
|
-
}
|
|
3842
|
-
}
|
|
3843
|
-
}
|
|
3844
|
-
start = end;
|
|
3845
|
-
}
|
|
3846
|
-
}
|
|
3847
|
-
function applyOffsetWindow(rows, indexes, window, samePartition) {
|
|
3848
|
-
const offset = window.offset ?? 1;
|
|
3849
|
-
const fallback = window.fallback ?? null;
|
|
3850
|
-
let start = 0;
|
|
3851
|
-
while (start < indexes.length) {
|
|
3852
|
-
let end = start + 1;
|
|
3853
|
-
while (end < indexes.length && samePartition(indexes[start] ?? 0, indexes[end] ?? 0)) {
|
|
3854
|
-
end += 1;
|
|
3855
|
-
}
|
|
3856
|
-
for (let position = start; position < end; position += 1) {
|
|
3857
|
-
const source = window.name === "LAG" ? position - offset : position + offset;
|
|
3858
|
-
const row = rows[indexes[position] ?? -1];
|
|
3859
|
-
if (row === void 0)
|
|
3860
|
-
continue;
|
|
3861
|
-
if (source < start || source >= end) {
|
|
3862
|
-
row[window.alias] = fallback;
|
|
3863
|
-
continue;
|
|
3864
|
-
}
|
|
3865
|
-
const sourceRow = rows[indexes[source] ?? -1];
|
|
3866
|
-
row[window.alias] = window.argumentAlias === void 0 ? fallback : sourceRow?.[window.argumentAlias] ?? null;
|
|
3867
|
-
}
|
|
3868
|
-
start = end;
|
|
3869
|
-
}
|
|
3870
|
-
}
|
|
3871
3650
|
function containsDistinctCount(expression) {
|
|
3872
3651
|
if (expression.kind === "call" && expression.distinct === true)
|
|
3873
3652
|
return true;
|
|
@@ -3888,75 +3667,6 @@ function containsFtsExpression(expression) {
|
|
|
3888
3667
|
return true;
|
|
3889
3668
|
return childExpressions(expression).some(containsFtsExpression);
|
|
3890
3669
|
}
|
|
3891
|
-
function applyWindowFunctions(result, windows, options = {}) {
|
|
3892
|
-
const rows = options.copyRows === false ? result.rows : result.rows.map((row) => ({ ...row }));
|
|
3893
|
-
for (const window of windows) {
|
|
3894
|
-
const partitionKeys = window.partitionAliases.map((alias) => rows.map((row) => comparable(row[alias] ?? null)));
|
|
3895
|
-
const orderKeys = window.orderAliases.map(({ alias }) => rows.map((row) => comparable(row[alias] ?? null)));
|
|
3896
|
-
const partitionColumns = partitionKeys.map((keys) => buildSortKeyColumn(keys.length, (index) => keys[index]));
|
|
3897
|
-
const orderColumns = orderKeys.map((keys) => buildSortKeyColumn(keys.length, (index) => keys[index]));
|
|
3898
|
-
const indexes = sortKeyIndexes(rows.length, [
|
|
3899
|
-
...partitionColumns.map((column) => ({ column, descending: false, nulls: void 0 })),
|
|
3900
|
-
...orderColumns.map((column, index) => {
|
|
3901
|
-
const term = window.orderAliases[index];
|
|
3902
|
-
return { column, descending: term?.direction === "desc", nulls: term?.nulls };
|
|
3903
|
-
})
|
|
3904
|
-
]);
|
|
3905
|
-
const samePartition = (left, right) => {
|
|
3906
|
-
for (const column of partitionColumns) {
|
|
3907
|
-
if (column.compare(left, right) !== 0)
|
|
3908
|
-
return false;
|
|
3909
|
-
}
|
|
3910
|
-
return true;
|
|
3911
|
-
};
|
|
3912
|
-
const sameOrderKeys = (left, right) => {
|
|
3913
|
-
for (const column of orderColumns) {
|
|
3914
|
-
if (column.compare(left, right) !== 0)
|
|
3915
|
-
return false;
|
|
3916
|
-
}
|
|
3917
|
-
return true;
|
|
3918
|
-
};
|
|
3919
|
-
if (window.name === "LAG" || window.name === "LEAD") {
|
|
3920
|
-
applyOffsetWindow(rows, indexes, window, samePartition);
|
|
3921
|
-
continue;
|
|
3922
|
-
}
|
|
3923
|
-
if (window.name === "NTILE" || window.name === "PERCENT_RANK" || window.name === "CUME_DIST") {
|
|
3924
|
-
applyDistributionWindow(rows, indexes, window, samePartition, sameOrderKeys);
|
|
3925
|
-
continue;
|
|
3926
|
-
}
|
|
3927
|
-
if (window.name !== "ROW_NUMBER" && window.name !== "RANK" && window.name !== "DENSE_RANK") {
|
|
3928
|
-
const argumentDomain = window.name === "AVG" && window.argumentAlias !== void 0 ? result.columnDomains[result.columns.indexOf(window.argumentAlias)] : void 0;
|
|
3929
|
-
applyAggregateWindow(rows, indexes, window, samePartition, sameOrderKeys, argumentDomain?.kind === "numeric" ? argumentDomain.scale : void 0);
|
|
3930
|
-
continue;
|
|
3931
|
-
}
|
|
3932
|
-
let rowNumber = 0;
|
|
3933
|
-
let rank = 0;
|
|
3934
|
-
let denseRank = 0;
|
|
3935
|
-
for (const [position, index] of indexes.entries()) {
|
|
3936
|
-
const previous = position > 0 ? indexes[position - 1] : void 0;
|
|
3937
|
-
if (previous === void 0 || !samePartition(previous, index)) {
|
|
3938
|
-
rowNumber = 1;
|
|
3939
|
-
rank = 1;
|
|
3940
|
-
denseRank = 1;
|
|
3941
|
-
} else {
|
|
3942
|
-
rowNumber += 1;
|
|
3943
|
-
if (!sameOrderKeys(previous, index)) {
|
|
3944
|
-
rank = rowNumber;
|
|
3945
|
-
denseRank += 1;
|
|
3946
|
-
}
|
|
3947
|
-
}
|
|
3948
|
-
const row = rows[index];
|
|
3949
|
-
if (row === void 0)
|
|
3950
|
-
continue;
|
|
3951
|
-
row[window.alias] = window.name === "ROW_NUMBER" ? rowNumber : window.name === "RANK" ? rank : denseRank;
|
|
3952
|
-
}
|
|
3953
|
-
}
|
|
3954
|
-
return {
|
|
3955
|
-
columns: [...result.columns, ...windows.map((window) => window.alias)],
|
|
3956
|
-
columnDomains: [...result.columnDomains, ...windows.map(() => null)],
|
|
3957
|
-
rows
|
|
3958
|
-
};
|
|
3959
|
-
}
|
|
3960
3670
|
function resolveDerivedRowTables(plan, tables, memory) {
|
|
3961
3671
|
const sources = [plan.base, ...plan.joins];
|
|
3962
3672
|
const needsDual = sources.some((source) => source.derived === void 0 && source.table === DUAL_TABLE);
|
|
@@ -4541,6 +4251,9 @@ function extractDatePart(field, value) {
|
|
|
4541
4251
|
}
|
|
4542
4252
|
if (value === null || value === void 0)
|
|
4543
4253
|
return null;
|
|
4254
|
+
const domainPart = temporalDomainPart(normalized, value);
|
|
4255
|
+
if (domainPart !== void 0)
|
|
4256
|
+
return domainPart;
|
|
4544
4257
|
let milliseconds;
|
|
4545
4258
|
if (value instanceof Date)
|
|
4546
4259
|
milliseconds = dateMilliseconds(value);
|
|
@@ -4563,7 +4276,7 @@ function extractDatePart(field, value) {
|
|
|
4563
4276
|
case "minute":
|
|
4564
4277
|
return Math.floor(timeOfDay / 6e4) % 60;
|
|
4565
4278
|
case "second":
|
|
4566
|
-
return
|
|
4279
|
+
return timeOfDay % 6e4 / 1e3;
|
|
4567
4280
|
case "milliseconds":
|
|
4568
4281
|
return timeOfDay % 6e4;
|
|
4569
4282
|
case "microseconds":
|
|
@@ -4863,7 +4576,7 @@ function isAggregateCall(expression) {
|
|
|
4863
4576
|
return expression.kind === "call" && aggregateNames.has(expression.name);
|
|
4864
4577
|
}
|
|
4865
4578
|
function groupedExpression(expression, grouped) {
|
|
4866
|
-
if (grouped.has(
|
|
4579
|
+
if (grouped.has(encodeQueryIdentity(expression)))
|
|
4867
4580
|
return true;
|
|
4868
4581
|
if (expression.kind === "column" || containsFtsExpression(expression))
|
|
4869
4582
|
return false;
|
|
@@ -4876,7 +4589,7 @@ function validateGrouping(plan) {
|
|
|
4876
4589
|
const grouped = plan.groupBy.length > 0 || plan.select.some((item) => hasAggregate(item.expression));
|
|
4877
4590
|
if (!grouped)
|
|
4878
4591
|
return;
|
|
4879
|
-
const groupExpressions = new Set(plan.groupBy.map((expression) =>
|
|
4592
|
+
const groupExpressions = new Set(plan.groupBy.map((expression) => encodeQueryIdentity(expression)));
|
|
4880
4593
|
for (const item of plan.select) {
|
|
4881
4594
|
if (hasAggregate(item.expression))
|
|
4882
4595
|
continue;
|
|
@@ -6577,7 +6290,7 @@ class Parser {
|
|
|
6577
6290
|
const outerOrderBy = [];
|
|
6578
6291
|
parts.orderBy.forEach((order, index) => {
|
|
6579
6292
|
const expression = order.expression;
|
|
6580
|
-
const output = expression.kind === "column" && !expression.reference.includes(".") ? parts.select.find((item) => item.alias === expression.reference) : parts.select.find((item) => item.expression.kind !== "wildcard" &&
|
|
6293
|
+
const output = expression.kind === "column" && !expression.reference.includes(".") ? parts.select.find((item) => item.alias === expression.reference) : parts.select.find((item) => item.expression.kind !== "wildcard" && encodeQueryIdentity(item.expression) === encodeQueryIdentity(expression));
|
|
6581
6294
|
let alias = output?.alias;
|
|
6582
6295
|
if (alias === void 0) {
|
|
6583
6296
|
alias = `\0distinct_on_order_${String(index)}`;
|
|
@@ -7021,6 +6734,47 @@ class Parser {
|
|
|
7021
6734
|
}
|
|
7022
6735
|
if (this.#peek().text === "(") {
|
|
7023
6736
|
const upper = table.toUpperCase();
|
|
6737
|
+
if (upper === "UNNEST") {
|
|
6738
|
+
this.#expectPunctuation("(");
|
|
6739
|
+
const array = this.#expression();
|
|
6740
|
+
this.#expectPunctuation(")");
|
|
6741
|
+
if (array.kind !== "call" || array.name !== "ARRAY")
|
|
6742
|
+
throw new TypeError("UNNEST currently requires an ARRAY constructor");
|
|
6743
|
+
if (array.arguments.some((argument) => expressionColumns(argument).length > 0 || hasAggregate(argument) || containsWindow(argument)))
|
|
6744
|
+
throw new TypeError("UNNEST ARRAY members cannot refer to table rows");
|
|
6745
|
+
let ordinality = false;
|
|
6746
|
+
if (this.#isKeyword("WITH")) {
|
|
6747
|
+
this.#keyword("WITH");
|
|
6748
|
+
this.#keyword("ORDINALITY");
|
|
6749
|
+
ordinality = true;
|
|
6750
|
+
}
|
|
6751
|
+
const alias2 = this.#sourceAlias() ?? "unnest";
|
|
6752
|
+
const blocks = (array.arguments.length === 0 ? [{ kind: "literal", value: null }] : array.arguments).map((expression, index) => ({
|
|
6753
|
+
sql: "(unnest row)",
|
|
6754
|
+
base: { table: DUAL_TABLE, alias: DUAL_TABLE },
|
|
6755
|
+
joins: [],
|
|
6756
|
+
select: [
|
|
6757
|
+
{ expression, alias: alias2 },
|
|
6758
|
+
...ordinality ? [
|
|
6759
|
+
{
|
|
6760
|
+
expression: { kind: "literal", value: index + 1 },
|
|
6761
|
+
alias: "ordinality"
|
|
6762
|
+
}
|
|
6763
|
+
] : []
|
|
6764
|
+
],
|
|
6765
|
+
predicates: [],
|
|
6766
|
+
groupBy: [],
|
|
6767
|
+
having: [],
|
|
6768
|
+
orderBy: [],
|
|
6769
|
+
...array.arguments.length === 0 ? { limit: 0 } : {}
|
|
6770
|
+
}));
|
|
6771
|
+
const derived = blocks.length === 1 ? blocks[0] : compoundSelectBlock("(unnest)", blocks, blocks.slice(1).map(() => "union all"), { orderBy: [] }, this.nextDerivedSequence);
|
|
6772
|
+
if (derived === void 0)
|
|
6773
|
+
throw new TypeError("UNNEST requires an ARRAY constructor");
|
|
6774
|
+
if (this.#punctuation("("))
|
|
6775
|
+
this.#applyColumnAliases(derived);
|
|
6776
|
+
return this.#derivedSource(derived, alias2);
|
|
6777
|
+
}
|
|
7024
6778
|
if (upper === "JSON_TABLE") {
|
|
7025
6779
|
this.#expectPunctuation("(");
|
|
7026
6780
|
const document = this.#expression();
|
|
@@ -7431,8 +7185,8 @@ class Parser {
|
|
|
7431
7185
|
if (this.#isKeyword("ESCAPE")) {
|
|
7432
7186
|
this.#keyword("ESCAPE");
|
|
7433
7187
|
const token2 = this.#take("string");
|
|
7434
|
-
if (Array.from(token2.text).length
|
|
7435
|
-
throw new TypeError("LIKE ESCAPE takes
|
|
7188
|
+
if (Array.from(token2.text).length > 1) {
|
|
7189
|
+
throw new TypeError("LIKE ESCAPE takes at most one character");
|
|
7436
7190
|
}
|
|
7437
7191
|
escape = token2.text;
|
|
7438
7192
|
}
|
|
@@ -7508,7 +7262,11 @@ class Parser {
|
|
|
7508
7262
|
}
|
|
7509
7263
|
const operator = this.#peek().text;
|
|
7510
7264
|
if (operator === "[") {
|
|
7511
|
-
|
|
7265
|
+
this.#index += 1;
|
|
7266
|
+
const index = this.#expression();
|
|
7267
|
+
this.#expectPunctuation("]");
|
|
7268
|
+
left = { kind: "call", name: "MINNOW_ARRAY_AT", arguments: [left, index] };
|
|
7269
|
+
continue;
|
|
7512
7270
|
}
|
|
7513
7271
|
const regexOperator = operator === "~" || operator === "~*" || operator === "!~" || operator === "!~*";
|
|
7514
7272
|
const precedence = operator === "::" ? 30 : operator === "^" ? 25 : operator === "*" || operator === "/" || operator === "%" ? 20 : operator === "+" || operator === "-" ? 10 : operator === "||" || operator === "->" || operator === "->>" || regexOperator ? 5 : -1;
|
|
@@ -8013,7 +7771,7 @@ class Parser {
|
|
|
8013
7771
|
return this.#booleanAggregate(upper);
|
|
8014
7772
|
}
|
|
8015
7773
|
const name = upper === "ANY_VALUE" ? "MIN" : functionSpellings.get(upper) ?? upper;
|
|
8016
|
-
if (name === "MINNOW_TUPLE_KEY" || name === "MINNOW_COLLATE" || name === "MINNOW_SINGLE_VALUE" || name === "MINNOW_JSON_GET" || name === "MINNOW_JSON_GET_TEXT" || name === "MINNOW_REGEX_MATCH") {
|
|
7774
|
+
if (name === "MINNOW_TUPLE_KEY" || name === "MINNOW_COLLATE" || name === "MINNOW_ARRAY_AT" || name === "MINNOW_ARRAY_FROM_JSON" || name === "MINNOW_ARRAY_ELEMENT" || name === "MINNOW_SINGLE_VALUE" || name === "MINNOW_JSON_GET" || name === "MINNOW_JSON_GET_TEXT" || name === "MINNOW_REGEX_MATCH") {
|
|
8017
7775
|
throw new TypeError(`Unsupported function: ${identifier}`);
|
|
8018
7776
|
}
|
|
8019
7777
|
if (!aggregateNames.has(name) && !scalarFunctionNames.has(name))
|
|
@@ -8176,13 +7934,17 @@ class Parser {
|
|
|
8176
7934
|
...frame === void 0 ? {} : { frame }
|
|
8177
7935
|
};
|
|
8178
7936
|
}
|
|
8179
|
-
|
|
7937
|
+
if (upper === "ARRAY_AGG" && args[0] !== void 0) {
|
|
7938
|
+
args[0] = { kind: "call", name: "MINNOW_ARRAY_ELEMENT", arguments: [args[0]] };
|
|
7939
|
+
}
|
|
7940
|
+
const call = {
|
|
8180
7941
|
kind: "call",
|
|
8181
7942
|
name,
|
|
8182
7943
|
arguments: args,
|
|
8183
7944
|
...distinct ? { distinct: true } : {},
|
|
8184
7945
|
...aggregateOrderBy === void 0 ? {} : { aggregateOrderBy }
|
|
8185
7946
|
};
|
|
7947
|
+
return upper === "ARRAY_AGG" ? { kind: "call", name: "MINNOW_ARRAY_FROM_JSON", arguments: [call] } : call;
|
|
8186
7948
|
}
|
|
8187
7949
|
let reference = identifier;
|
|
8188
7950
|
if (this.#punctuation(".")) {
|
|
@@ -8346,6 +8108,8 @@ class Parser {
|
|
|
8346
8108
|
if (unit === "groups" && orderBy.length === 0) {
|
|
8347
8109
|
throw new TypeError("GROUPS frames require ORDER BY inside OVER (...)");
|
|
8348
8110
|
}
|
|
8111
|
+
if (unit === "range" && (start.offset !== void 0 || end.offset !== void 0) && orderBy.length !== 1)
|
|
8112
|
+
throw new TypeError("Offset RANGE frames require exactly one ORDER BY expression");
|
|
8349
8113
|
let exclude;
|
|
8350
8114
|
if (this.#isKeyword("EXCLUDE")) {
|
|
8351
8115
|
this.#keyword("EXCLUDE");
|
|
@@ -8391,11 +8155,8 @@ class Parser {
|
|
|
8391
8155
|
return { kind: "current-row" };
|
|
8392
8156
|
}
|
|
8393
8157
|
const offset = Number(this.#take("number").text);
|
|
8394
|
-
if (!Number.
|
|
8395
|
-
throw new TypeError("Window frame offsets must be non-negative integers");
|
|
8396
|
-
}
|
|
8397
|
-
if (unit === "range") {
|
|
8398
|
-
throw new TypeError("RANGE frames take only UNBOUNDED and CURRENT ROW bounds; use ROWS");
|
|
8158
|
+
if (!Number.isFinite(offset) || offset < 0 || unit !== "range" && !Number.isInteger(offset)) {
|
|
8159
|
+
throw new TypeError("Window frame offsets must be non-negative integers, or numeric RANGE offsets");
|
|
8399
8160
|
}
|
|
8400
8161
|
if (this.#isKeyword("PRECEDING")) {
|
|
8401
8162
|
this.#keyword("PRECEDING");
|
|
@@ -8468,7 +8229,7 @@ function desugarGroupingSets(parts, nextSequence) {
|
|
|
8468
8229
|
}
|
|
8469
8230
|
}
|
|
8470
8231
|
}
|
|
8471
|
-
const signatureOf = (expression) =>
|
|
8232
|
+
const signatureOf = (expression) => encodeQueryIdentity(expression);
|
|
8472
8233
|
const universe = new Set(sets.flat().map(signatureOf));
|
|
8473
8234
|
const members = sets.map((set) => {
|
|
8474
8235
|
const setSignatures = new Set(set.map(signatureOf));
|
|
@@ -8707,6 +8468,108 @@ function unifyPlanGroupedReferences(plan, columnsOf) {
|
|
|
8707
8468
|
return owner === void 0 ? void 0 : `${owner}.${reference}`;
|
|
8708
8469
|
});
|
|
8709
8470
|
}
|
|
8471
|
+
function desugarComposedFullJoin(parts, join, nextSequence) {
|
|
8472
|
+
const marker = `(full marker ${String(nextSequence())})`;
|
|
8473
|
+
const plain = {
|
|
8474
|
+
sql: parts.sql,
|
|
8475
|
+
distinct: false,
|
|
8476
|
+
predicates: [],
|
|
8477
|
+
groupBy: [],
|
|
8478
|
+
having: [],
|
|
8479
|
+
orderBy: []
|
|
8480
|
+
};
|
|
8481
|
+
const marked = derivedTableSource(assembleSelectBlock({
|
|
8482
|
+
...plain,
|
|
8483
|
+
base: parts.base,
|
|
8484
|
+
joins: [],
|
|
8485
|
+
select: [
|
|
8486
|
+
{ expression: { kind: "wildcard", table: parts.base.alias }, alias: "*" },
|
|
8487
|
+
{ expression: { kind: "literal", value: 1 }, alias: marker }
|
|
8488
|
+
]
|
|
8489
|
+
}, nextSequence), parts.base.alias, nextSequence);
|
|
8490
|
+
const columns = /* @__PURE__ */ new Map();
|
|
8491
|
+
const gather = (expression) => {
|
|
8492
|
+
if (expression.kind === "column" && !columns.has(expression.reference))
|
|
8493
|
+
columns.set(expression.reference, `(full column ${String(columns.size)})`);
|
|
8494
|
+
for (const child of childExpressions(expression))
|
|
8495
|
+
gather(child);
|
|
8496
|
+
};
|
|
8497
|
+
for (const item of parts.select)
|
|
8498
|
+
gather(item.expression);
|
|
8499
|
+
for (const expression of parts.groupBy)
|
|
8500
|
+
gather(expression);
|
|
8501
|
+
for (const set of parts.groupingSets ?? [])
|
|
8502
|
+
for (const expression of set)
|
|
8503
|
+
gather(expression);
|
|
8504
|
+
for (const predicate of [...parts.predicates, ...parts.having]) {
|
|
8505
|
+
gather(predicate.left);
|
|
8506
|
+
gather(predicate.right);
|
|
8507
|
+
}
|
|
8508
|
+
const outputs = new Set(parts.select.map((item) => item.alias));
|
|
8509
|
+
for (const order of parts.orderBy)
|
|
8510
|
+
if (order.expression.kind !== "column" || !outputs.has(order.expression.reference))
|
|
8511
|
+
gather(order.expression);
|
|
8512
|
+
const select = [...columns].map(([reference, alias]) => ({
|
|
8513
|
+
expression: { kind: "column", reference },
|
|
8514
|
+
alias
|
|
8515
|
+
}));
|
|
8516
|
+
if (select.length === 0)
|
|
8517
|
+
select.push({ expression: { kind: "literal", value: 1 }, alias: "(full row)" });
|
|
8518
|
+
const { full, kind, left, right, on, ...rightSource } = join;
|
|
8519
|
+
void full;
|
|
8520
|
+
void kind;
|
|
8521
|
+
const matched = assembleSelectBlock({
|
|
8522
|
+
...plain,
|
|
8523
|
+
base: marked,
|
|
8524
|
+
joins: [{ ...rightSource, kind: "left", left, right, ...on === void 0 ? {} : { on } }],
|
|
8525
|
+
select
|
|
8526
|
+
}, nextSequence);
|
|
8527
|
+
const unmatched = assembleSelectBlock({
|
|
8528
|
+
...plain,
|
|
8529
|
+
base: rightSource,
|
|
8530
|
+
joins: [{ ...marked, kind: "left", left, right, ...on === void 0 ? {} : { on } }],
|
|
8531
|
+
select,
|
|
8532
|
+
predicates: [
|
|
8533
|
+
{
|
|
8534
|
+
left: { kind: "column", reference: `${marked.alias}.${marker}` },
|
|
8535
|
+
operator: "IS NULL",
|
|
8536
|
+
right: { kind: "literal", value: null }
|
|
8537
|
+
}
|
|
8538
|
+
]
|
|
8539
|
+
}, nextSequence);
|
|
8540
|
+
const combined = compoundSelectBlock(parts.sql, [matched, unmatched], ["union all"], { orderBy: [] }, nextSequence);
|
|
8541
|
+
const source = derivedTableSource(combined, `(full result ${String(nextSequence())})`, nextSequence);
|
|
8542
|
+
const rewrite = (expression) => {
|
|
8543
|
+
if (expression.kind === "column") {
|
|
8544
|
+
const column = columns.get(expression.reference);
|
|
8545
|
+
if (column !== void 0)
|
|
8546
|
+
return { kind: "column", reference: `${source.alias}.${column}` };
|
|
8547
|
+
}
|
|
8548
|
+
return mapChildExpressions(expression, rewrite);
|
|
8549
|
+
};
|
|
8550
|
+
return assembleSelectBlock({
|
|
8551
|
+
...parts,
|
|
8552
|
+
base: source,
|
|
8553
|
+
joins: [],
|
|
8554
|
+
select: parts.select.map((item) => ({ ...item, expression: rewrite(item.expression) })),
|
|
8555
|
+
predicates: parts.predicates.map((predicate) => ({
|
|
8556
|
+
...predicate,
|
|
8557
|
+
left: rewrite(predicate.left),
|
|
8558
|
+
right: rewrite(predicate.right)
|
|
8559
|
+
})),
|
|
8560
|
+
groupBy: parts.groupBy.map(rewrite),
|
|
8561
|
+
having: parts.having.map((predicate) => ({
|
|
8562
|
+
...predicate,
|
|
8563
|
+
left: rewrite(predicate.left),
|
|
8564
|
+
right: rewrite(predicate.right)
|
|
8565
|
+
})),
|
|
8566
|
+
orderBy: parts.orderBy.map((order) => ({
|
|
8567
|
+
...order,
|
|
8568
|
+
expression: order.expression.kind === "column" && outputs.has(order.expression.reference) ? order.expression : rewrite(order.expression)
|
|
8569
|
+
})),
|
|
8570
|
+
...parts.groupingSets === void 0 ? {} : { groupingSets: parts.groupingSets.map((set) => set.map(rewrite)) }
|
|
8571
|
+
}, nextSequence);
|
|
8572
|
+
}
|
|
8710
8573
|
function desugarFullJoin(parts, nextSequence) {
|
|
8711
8574
|
const join = parts.joins[0];
|
|
8712
8575
|
if (parts.joins.length !== 1 || join?.full !== true) {
|
|
@@ -8716,10 +8579,10 @@ function desugarFullJoin(parts, nextSequence) {
|
|
|
8716
8579
|
throw new TypeError("FULL JOIN cannot be combined with SELECT *");
|
|
8717
8580
|
}
|
|
8718
8581
|
if (parts.groupBy.length > 0 || parts.having.length > 0 || parts.distinct || parts.select.some((item) => hasAggregate(item.expression) || containsWindow(item.expression))) {
|
|
8719
|
-
|
|
8582
|
+
return desugarComposedFullJoin(parts, join, nextSequence);
|
|
8720
8583
|
}
|
|
8721
8584
|
if (join.on !== void 0) {
|
|
8722
|
-
|
|
8585
|
+
return desugarComposedFullJoin(parts, join, nextSequence);
|
|
8723
8586
|
}
|
|
8724
8587
|
const outputAlias = (reference) => {
|
|
8725
8588
|
const exact = parts.select.find((item) => item.alias === reference || item.expression.kind === "column" && item.expression.reference === reference);
|
|
@@ -8775,9 +8638,6 @@ function desugarFullJoin(parts, nextSequence) {
|
|
|
8775
8638
|
}
|
|
8776
8639
|
function assembleSelectBlock(parts, nextSequence) {
|
|
8777
8640
|
if (parts.select.some((item) => item.expression.kind === "wildcard")) {
|
|
8778
|
-
if (parts.joins.some((join) => join.full === true)) {
|
|
8779
|
-
throw new TypeError("FULL JOIN cannot be combined with SELECT *");
|
|
8780
|
-
}
|
|
8781
8641
|
return {
|
|
8782
8642
|
sql: parts.sql,
|
|
8783
8643
|
base: parts.base,
|
|
@@ -9408,19 +9268,16 @@ function assembleOrderByExpressionBlock(parts, nextSequence) {
|
|
|
9408
9268
|
for (const order of parts.orderBy) {
|
|
9409
9269
|
if (!orderNeedsHiddenColumn(order.expression, parts))
|
|
9410
9270
|
continue;
|
|
9411
|
-
if (containsWindow(order.expression)) {
|
|
9412
|
-
throw new TypeError("Window functions are only allowed in the select list");
|
|
9413
|
-
}
|
|
9414
9271
|
if (order.expression.kind === "literal") {
|
|
9415
9272
|
throw new TypeError("ORDER BY position is outside the select list");
|
|
9416
9273
|
}
|
|
9417
9274
|
}
|
|
9418
|
-
const selectSignatures = new Map(parts.select.map((item) => [
|
|
9275
|
+
const selectSignatures = new Map(parts.select.map((item) => [encodeQueryIdentity(item.expression), item.alias]));
|
|
9419
9276
|
const hiddenItems = [];
|
|
9420
9277
|
const rewrittenOrder = parts.orderBy.map((order) => {
|
|
9421
9278
|
if (!orderNeedsHiddenColumn(order.expression, parts))
|
|
9422
9279
|
return order;
|
|
9423
|
-
const existingAlias = selectSignatures.get(
|
|
9280
|
+
const existingAlias = selectSignatures.get(encodeQueryIdentity(order.expression));
|
|
9424
9281
|
if (existingAlias !== void 0) {
|
|
9425
9282
|
return {
|
|
9426
9283
|
...order,
|
|
@@ -9596,16 +9453,24 @@ function validateOffset(offset) {
|
|
|
9596
9453
|
}
|
|
9597
9454
|
function desugarWindows(sql, base, joins, select, predicates, groupBy, having, tail, nextSequence) {
|
|
9598
9455
|
const grouped = groupBy.length > 0 || select.some((item) => hasAggregate(item.expression));
|
|
9599
|
-
const groupExpressions = new Set(groupBy.map((expression) =>
|
|
9600
|
-
const readableWhenGrouped = (expression) => hasAggregate(expression) || expressionColumns(expression).length === 0 || groupExpressions.has(
|
|
9456
|
+
const groupExpressions = new Set(groupBy.map((expression) => encodeQueryIdentity(expression)));
|
|
9457
|
+
const readableWhenGrouped = (expression) => hasAggregate(expression) || expressionColumns(expression).length === 0 || groupExpressions.has(encodeQueryIdentity(expression));
|
|
9601
9458
|
const internalAliases = select.map((item, index) => item.alias.includes(".") ? `(window visible ${String(index + 1)})` : item.alias);
|
|
9602
9459
|
const innerSelect = select.flatMap((item, index) => containsWindow(item.expression) ? [] : [{ ...item, alias: internalAliases[index] ?? item.alias }]);
|
|
9603
9460
|
const windows = [];
|
|
9604
9461
|
let hidden = 0;
|
|
9462
|
+
const hiddenExpressions = /* @__PURE__ */ new Map();
|
|
9463
|
+
const repeatable = (expression) => !(expression.kind === "call" && volatileScalarFunctionNames.has(expression.name)) && childExpressions(expression).every(repeatable);
|
|
9605
9464
|
const hide = (expression) => {
|
|
9465
|
+
const key = repeatable(expression) ? encodeQueryIdentity(expression) : void 0;
|
|
9466
|
+
const existing = key === void 0 ? void 0 : hiddenExpressions.get(key);
|
|
9467
|
+
if (existing !== void 0)
|
|
9468
|
+
return existing;
|
|
9606
9469
|
hidden += 1;
|
|
9607
9470
|
const alias = `(window ${String(hidden)})`;
|
|
9608
9471
|
innerSelect.push({ expression, alias });
|
|
9472
|
+
if (key !== void 0)
|
|
9473
|
+
hiddenExpressions.set(key, alias);
|
|
9609
9474
|
return alias;
|
|
9610
9475
|
};
|
|
9611
9476
|
const registerWindow = (expression, alias) => {
|
|
@@ -10019,7 +9884,7 @@ export {
|
|
|
10019
9884
|
annotateAvgArgumentScales,
|
|
10020
9885
|
annotateIntegerDivision,
|
|
10021
9886
|
annotatePlanIntegerDivision,
|
|
10022
|
-
applyWindowFunctions,
|
|
9887
|
+
applyWindowFunctions2 as applyWindowFunctions,
|
|
10023
9888
|
assembleSelectBlock,
|
|
10024
9889
|
bindPendingSelectShapes,
|
|
10025
9890
|
bindPlanParameters,
|
|
@@ -10049,6 +9914,7 @@ export {
|
|
|
10049
9914
|
evaluateRowExpression,
|
|
10050
9915
|
executeQuery,
|
|
10051
9916
|
executeRowQuery,
|
|
9917
|
+
executeRowQueryInternal,
|
|
10052
9918
|
expandFtsColumns,
|
|
10053
9919
|
expandNaturalJoins,
|
|
10054
9920
|
expandRowReferences,
|