documents.js 1.63.0 → 1.65.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/README.md +124 -25
- package/dist/convert/convert.cjs +3 -3
- package/dist/convert/convert.d.cts +5 -2
- package/dist/convert/convert.d.ts +4 -1
- package/dist/convert/convert.js +3 -3
- package/dist/edit/docx/content.d.cts +1 -1
- package/dist/edit/docx/content.d.ts +1 -1
- package/dist/edit/docx/paragraph.d.cts +3 -3
- package/dist/edit/docx/paragraph.d.ts +3 -3
- package/dist/edit/ods/address.cjs +42 -24
- package/dist/edit/ods/address.d.cts +12 -2
- package/dist/edit/ods/address.d.ts +12 -2
- package/dist/edit/ods/address.js +39 -25
- package/dist/edit/ods/column-row.cjs +68 -6
- package/dist/edit/ods/column-row.d.cts +5 -1
- package/dist/edit/ods/column-row.d.ts +5 -1
- package/dist/edit/ods/column-row.js +68 -10
- package/dist/edit/ods/content.cjs +10 -2
- package/dist/edit/ods/content.js +10 -2
- package/dist/edit/ods/floating.cjs +90 -0
- package/dist/edit/ods/floating.d.cts +7 -0
- package/dist/edit/ods/floating.d.ts +7 -0
- package/dist/edit/ods/floating.js +88 -0
- package/dist/edit/ods/print-settings.cjs +158 -2
- package/dist/edit/ods/print-settings.js +159 -3
- package/dist/edit/ods/sheet.cjs +13 -0
- package/dist/edit/ods/sheet.d.cts +5 -1
- package/dist/edit/ods/sheet.d.ts +5 -1
- package/dist/edit/ods/sheet.js +14 -1
- package/dist/edit/odt/content.cjs +8 -4
- package/dist/edit/odt/content.js +8 -4
- package/dist/edit/odt/editor.cjs +7 -0
- package/dist/edit/odt/editor.d.cts +3 -0
- package/dist/edit/odt/editor.d.ts +3 -0
- package/dist/edit/odt/editor.js +7 -0
- package/dist/edit/odt/formula.cjs +19 -0
- package/dist/edit/odt/formula.d.cts +8 -0
- package/dist/edit/odt/formula.d.ts +8 -0
- package/dist/edit/odt/formula.js +17 -0
- package/dist/evaluate-BU5N1Jk1.d.ts +11 -0
- package/dist/evaluate-BhMXY6DM.d.cts +11 -0
- package/dist/firebird/backup.d.cts +1 -1
- package/dist/firebird/backup.d.ts +1 -1
- package/dist/firebird/data.cjs +30 -16
- package/dist/firebird/data.d.cts +3 -2
- package/dist/firebird/data.d.ts +3 -2
- package/dist/firebird/data.js +30 -17
- package/dist/fonts/ooxml.d.cts +1 -1
- package/dist/fonts/registry.d.cts +1 -1
- package/dist/formula-CgydMiTt.d.ts +10 -0
- package/dist/formula-CiwPw8nY.d.cts +10 -0
- package/dist/hsqldb/binary-script.d.cts +1 -1
- package/dist/hsqldb/binary-script.d.ts +1 -1
- package/dist/hsqldb/cache.d.cts +1 -1
- package/dist/hsqldb/cache.d.ts +1 -1
- package/dist/hsqldb/rowformat.cjs +22 -13
- package/dist/hsqldb/rowformat.js +22 -13
- package/dist/hsqldb/script.d.cts +1 -18
- package/dist/hsqldb/script.d.ts +1 -18
- package/dist/index.cjs +56 -0
- package/dist/index.d.cts +24 -10
- package/dist/index.d.ts +23 -9
- package/dist/index.js +15 -2
- package/dist/layout/sheets.cjs +30 -24
- package/dist/layout/sheets.js +31 -25
- package/dist/layout/slides.cjs +6 -4
- package/dist/layout/slides.js +7 -5
- package/dist/lexer-DGPWx_f2.d.cts +42 -0
- package/dist/lexer-DGPWx_f2.d.ts +42 -0
- package/dist/mathml/compose.cjs +8 -0
- package/dist/mathml/compose.js +8 -0
- package/dist/mathml/layout-types.d.cts +14 -2
- package/dist/mathml/layout-types.d.ts +14 -2
- package/dist/mathml/layout.cjs +62 -3
- package/dist/mathml/layout.d.cts +1 -1
- package/dist/mathml/layout.d.ts +1 -1
- package/dist/mathml/layout.js +62 -3
- package/dist/mathml/metrics.d.cts +17 -1
- package/dist/mathml/metrics.d.ts +17 -1
- package/dist/mathml/operators.cjs +1 -1
- package/dist/mathml/operators.js +1 -1
- package/dist/mathml/variant.d.cts +1 -6
- package/dist/mathml/variant.d.ts +1 -6
- package/dist/odb/csv.d.cts +1 -1
- package/dist/odb/csv.d.ts +1 -1
- package/dist/odb/formula/definition.cjs +49 -0
- package/dist/odb/formula/definition.d.cts +8 -0
- package/dist/odb/formula/definition.d.ts +8 -0
- package/dist/odb/formula/definition.js +46 -0
- package/dist/odb/formula/errors.cjs +47 -0
- package/dist/odb/formula/errors.d.cts +21 -0
- package/dist/odb/formula/errors.d.ts +21 -0
- package/dist/odb/formula/errors.js +43 -0
- package/dist/odb/formula/evaluate.cjs +283 -0
- package/dist/odb/formula/evaluate.d.cts +43 -0
- package/dist/odb/formula/evaluate.d.ts +43 -0
- package/dist/odb/formula/evaluate.js +281 -0
- package/dist/odb/formula/parser.cjs +198 -0
- package/dist/odb/formula/parser.d.cts +29 -0
- package/dist/odb/formula/parser.d.ts +29 -0
- package/dist/odb/formula/parser.js +197 -0
- package/dist/odb/read.d.cts +1 -1
- package/dist/odb/read.d.ts +1 -1
- package/dist/odb/report/content.cjs +27 -0
- package/dist/odb/report/content.d.cts +14 -0
- package/dist/odb/report/content.d.ts +14 -0
- package/dist/odb/report/content.js +25 -0
- package/dist/odb/report/render.cjs +106 -0
- package/dist/odb/report/render.d.cts +7 -0
- package/dist/odb/report/render.d.ts +7 -0
- package/dist/odb/report/render.js +106 -0
- package/dist/odb/report/source.cjs +45 -0
- package/dist/odb/report/source.d.cts +14 -0
- package/dist/odb/report/source.d.ts +14 -0
- package/dist/odb/report/source.js +42 -0
- package/dist/odb/spreadsheet.d.cts +1 -1
- package/dist/odb/spreadsheet.d.ts +1 -1
- package/dist/odb/sql/errors.cjs +38 -0
- package/dist/odb/sql/errors.d.cts +17 -0
- package/dist/odb/sql/errors.d.ts +17 -0
- package/dist/odb/sql/errors.js +35 -0
- package/dist/odb/sql/evaluate.cjs +312 -0
- package/dist/odb/sql/evaluate.d.cts +2 -0
- package/dist/odb/sql/evaluate.d.ts +2 -0
- package/dist/odb/sql/evaluate.js +311 -0
- package/dist/odb/sql/lexer.cjs +282 -0
- package/dist/odb/sql/lexer.d.cts +2 -0
- package/dist/odb/sql/lexer.d.ts +2 -0
- package/dist/odb/sql/lexer.js +281 -0
- package/dist/odb/sql/parser.cjs +479 -0
- package/dist/odb/sql/parser.d.cts +2 -0
- package/dist/odb/sql/parser.d.ts +2 -0
- package/dist/odb/sql/parser.js +478 -0
- package/dist/odb/values.cjs +75 -0
- package/dist/odb/values.d.cts +2 -0
- package/dist/odb/values.d.ts +2 -0
- package/dist/odb/values.js +69 -0
- package/dist/odf/odt/read.cjs +43 -21
- package/dist/odf/odt/read.js +43 -21
- package/dist/odf-package/formula.cjs +59 -0
- package/dist/odf-package/formula.d.cts +11 -0
- package/dist/odf-package/formula.d.ts +11 -0
- package/dist/odf-package/formula.js +58 -0
- package/dist/odf-package/manifest.cjs +29 -0
- package/dist/odf-package/manifest.d.cts +5 -2
- package/dist/odf-package/manifest.d.ts +5 -2
- package/dist/odf-package/manifest.js +30 -2
- package/dist/odf-package/media.cjs +2 -1
- package/dist/odf-package/media.js +2 -2
- package/dist/omml/read.cjs +265 -0
- package/dist/omml/read.d.cts +12 -0
- package/dist/omml/read.d.ts +12 -0
- package/dist/omml/read.js +263 -0
- package/dist/omml/shared.cjs +67 -0
- package/dist/omml/shared.d.cts +2 -0
- package/dist/omml/shared.d.ts +2 -0
- package/dist/omml/shared.js +64 -0
- package/dist/omml/write.cjs +3 -51
- package/dist/omml/write.d.cts +2 -2
- package/dist/omml/write.d.ts +2 -2
- package/dist/omml/write.js +1 -49
- package/dist/ooxml/docx/extras.cjs +15 -0
- package/dist/ooxml/docx/extras.d.cts +12 -0
- package/dist/ooxml/docx/extras.d.ts +12 -0
- package/dist/ooxml/docx/extras.js +14 -0
- package/dist/ooxml/docx/formula.cjs +109 -0
- package/dist/ooxml/docx/formula.d.cts +2 -0
- package/dist/ooxml/docx/formula.d.ts +2 -0
- package/dist/ooxml/docx/formula.js +108 -0
- package/dist/ooxml/docx/read.cjs +10 -2
- package/dist/ooxml/docx/read.d.cts +6 -2
- package/dist/ooxml/docx/read.d.ts +6 -2
- package/dist/ooxml/docx/read.js +11 -3
- package/dist/parser-B9vC1T6T.d.cts +101 -0
- package/dist/parser-DQSgYJzQ.d.ts +101 -0
- package/dist/{registry-CtPhMD0f.d.cts → registry-lqJej0Jr.d.cts} +1 -1
- package/dist/script-DsGGIzGU.d.cts +19 -0
- package/dist/script-DsGGIzGU.d.ts +19 -0
- package/dist/shared-DLZ3IQUl.d.cts +15 -0
- package/dist/shared-DUOzjwcL.d.ts +15 -0
- package/dist/values-BetetbaH.d.cts +23 -0
- package/dist/values-BetetbaH.d.ts +23 -0
- package/dist/variant-BMrebjMw.d.cts +7 -0
- package/dist/variant-BMrebjMw.d.ts +7 -0
- package/dist/{write-DuyfnbL_.d.cts → write-BQ6SK8r8.d.cts} +2 -6
- package/dist/{write-Zqwx6pka.d.ts → write-THs7ipBq.d.ts} +2 -6
- package/package.json +2 -2
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { CELL_NULL, aggregateCellValues, cellComparisonKey, compareCellValues } from "../values.js";
|
|
2
|
+
import { HsqldbSqlEvaluationError } from "./errors.js";
|
|
3
|
+
//#region src/odb/sql/evaluate.ts
|
|
4
|
+
function sqlFailure(sql) {
|
|
5
|
+
return (message) => new HsqldbSqlEvaluationError(message, sql);
|
|
6
|
+
}
|
|
7
|
+
function compareValues(left, right, sql) {
|
|
8
|
+
return compareCellValues(left, right, sqlFailure(sql));
|
|
9
|
+
}
|
|
10
|
+
function truthOfComparison(ordering, operator) {
|
|
11
|
+
switch (operator) {
|
|
12
|
+
case "=": return ordering === 0 ? "true" : "false";
|
|
13
|
+
case "<>": return ordering === 0 ? "false" : "true";
|
|
14
|
+
case "<": return ordering < 0 ? "true" : "false";
|
|
15
|
+
case ">": return ordering > 0 ? "true" : "false";
|
|
16
|
+
case "<=": return ordering <= 0 ? "true" : "false";
|
|
17
|
+
case ">=": return ordering >= 0 ? "true" : "false";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function notTruth(truth) {
|
|
21
|
+
if (truth === "true") return "false";
|
|
22
|
+
return truth === "false" ? "true" : "unknown";
|
|
23
|
+
}
|
|
24
|
+
function andTruth(left, right) {
|
|
25
|
+
if (left === "false" || right === "false") return "false";
|
|
26
|
+
return left === "true" && right === "true" ? "true" : "unknown";
|
|
27
|
+
}
|
|
28
|
+
function orTruth(left, right) {
|
|
29
|
+
if (left === "true" || right === "true") return "true";
|
|
30
|
+
return left === "false" && right === "false" ? "false" : "unknown";
|
|
31
|
+
}
|
|
32
|
+
function literalToValue(literal) {
|
|
33
|
+
switch (literal.kind) {
|
|
34
|
+
case "number": return {
|
|
35
|
+
kind: "number",
|
|
36
|
+
value: literal.value
|
|
37
|
+
};
|
|
38
|
+
case "string": return {
|
|
39
|
+
kind: "string",
|
|
40
|
+
value: literal.value
|
|
41
|
+
};
|
|
42
|
+
case "boolean": return {
|
|
43
|
+
kind: "boolean",
|
|
44
|
+
value: literal.value
|
|
45
|
+
};
|
|
46
|
+
case "null": return CELL_NULL;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function likePatternToRegExp(pattern) {
|
|
50
|
+
let source = "^";
|
|
51
|
+
for (const character of pattern) if (character === "%") source += "[\\s\\S]*";
|
|
52
|
+
else if (character === "_") source += "[\\s\\S]";
|
|
53
|
+
else source += character.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
54
|
+
return new RegExp(`${source}$`);
|
|
55
|
+
}
|
|
56
|
+
function resolveName(candidates, ref, what, sql) {
|
|
57
|
+
const exact = candidates.find((candidate) => candidate === ref.name);
|
|
58
|
+
if (exact !== void 0) return exact;
|
|
59
|
+
if (!ref.quoted) {
|
|
60
|
+
const folded = candidates.filter((candidate) => candidate.toUpperCase() === ref.name);
|
|
61
|
+
if (folded.length > 1) throw new HsqldbSqlEvaluationError(`${what} "${ref.name}" is ambiguous -- it matches ${folded.join(", ")} case-insensitively`, sql);
|
|
62
|
+
const only = folded[0];
|
|
63
|
+
if (only !== void 0) return only;
|
|
64
|
+
}
|
|
65
|
+
throw new HsqldbSqlEvaluationError(`${what} "${ref.name}" not found -- available: ${candidates.length === 0 ? "(none)" : candidates.join(", ")}`, sql);
|
|
66
|
+
}
|
|
67
|
+
function resolveTable(tables, ref, sql) {
|
|
68
|
+
const resolvedName = resolveName(tables.map((table) => table.tableName), ref, "table", sql);
|
|
69
|
+
const table = tables.find((candidate) => candidate.tableName === resolvedName);
|
|
70
|
+
if (table === void 0) throw new HsqldbSqlEvaluationError(`table "${resolvedName}" not found`, sql);
|
|
71
|
+
return table;
|
|
72
|
+
}
|
|
73
|
+
var ColumnResolver = class {
|
|
74
|
+
table;
|
|
75
|
+
sql;
|
|
76
|
+
cache = /* @__PURE__ */ new Map();
|
|
77
|
+
columnNames;
|
|
78
|
+
constructor(table, sql) {
|
|
79
|
+
this.table = table;
|
|
80
|
+
this.sql = sql;
|
|
81
|
+
this.columnNames = table.columns.map((column) => column.name);
|
|
82
|
+
}
|
|
83
|
+
nameAt(index) {
|
|
84
|
+
const name = this.columnNames[index];
|
|
85
|
+
if (name === void 0) throw new HsqldbSqlEvaluationError(`column index ${String(index)} is outside table "${this.table.tableName}"`, this.sql);
|
|
86
|
+
return name;
|
|
87
|
+
}
|
|
88
|
+
indexOf(ref) {
|
|
89
|
+
const cached = this.cache.get(ref);
|
|
90
|
+
if (cached !== void 0) return cached;
|
|
91
|
+
if (ref.qualifier !== void 0) {
|
|
92
|
+
if (resolveName([this.table.tableName], ref.qualifier, "table qualifier", this.sql) !== this.table.tableName) throw new HsqldbSqlEvaluationError(`table qualifier "${ref.qualifier.name}" does not name the table in FROM ("${this.table.tableName}")`, this.sql);
|
|
93
|
+
}
|
|
94
|
+
const index = this.columnNames.indexOf(resolveName(this.columnNames, ref.column, "column", this.sql));
|
|
95
|
+
this.cache.set(ref, index);
|
|
96
|
+
return index;
|
|
97
|
+
}
|
|
98
|
+
valueAt(index, row) {
|
|
99
|
+
const value = row[index];
|
|
100
|
+
if (value === void 0) throw new HsqldbSqlEvaluationError(`malformed table "${this.table.tableName}": a row carries ${String(row.length)} values but the table declares ${String(this.columnNames.length)} columns`, this.sql);
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
valueOf(ref, row) {
|
|
104
|
+
return this.valueAt(this.indexOf(ref), row);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function operandValue(operand, row, resolver) {
|
|
108
|
+
return operand.kind === "literal" ? literalToValue(operand.literal) : resolver.valueOf(operand.column, row);
|
|
109
|
+
}
|
|
110
|
+
function evaluateLike(value, pattern, sql) {
|
|
111
|
+
const key = cellComparisonKey(value);
|
|
112
|
+
if (key === void 0) return "unknown";
|
|
113
|
+
if (key.valueClass !== "text") throw new HsqldbSqlEvaluationError(`LIKE requires a text value, but its left operand is a ${key.valueClass} value`, sql);
|
|
114
|
+
return likePatternToRegExp(pattern).test(key.text) ? "true" : "false";
|
|
115
|
+
}
|
|
116
|
+
function evaluateIn(value, values, sql) {
|
|
117
|
+
if (value.kind === "empty") return "unknown";
|
|
118
|
+
let sawNull = false;
|
|
119
|
+
for (const literal of values) {
|
|
120
|
+
const candidate = literalToValue(literal);
|
|
121
|
+
if (candidate.kind === "empty") {
|
|
122
|
+
sawNull = true;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (compareValues(value, candidate, sql) === 0) return "true";
|
|
126
|
+
}
|
|
127
|
+
return sawNull ? "unknown" : "false";
|
|
128
|
+
}
|
|
129
|
+
function evaluatePredicate(predicate, row, resolver, sql) {
|
|
130
|
+
switch (predicate.kind) {
|
|
131
|
+
case "comparison": {
|
|
132
|
+
const left = operandValue(predicate.left, row, resolver);
|
|
133
|
+
const right = operandValue(predicate.right, row, resolver);
|
|
134
|
+
if (left.kind === "empty" || right.kind === "empty") return "unknown";
|
|
135
|
+
return truthOfComparison(compareValues(left, right, sql), predicate.operator);
|
|
136
|
+
}
|
|
137
|
+
case "isNull": {
|
|
138
|
+
const isNull = operandValue(predicate.operand, row, resolver).kind === "empty";
|
|
139
|
+
return (predicate.negated ? !isNull : isNull) ? "true" : "false";
|
|
140
|
+
}
|
|
141
|
+
case "like": {
|
|
142
|
+
const truth = evaluateLike(operandValue(predicate.operand, row, resolver), predicate.pattern, sql);
|
|
143
|
+
return predicate.negated ? notTruth(truth) : truth;
|
|
144
|
+
}
|
|
145
|
+
case "in": {
|
|
146
|
+
const truth = evaluateIn(operandValue(predicate.operand, row, resolver), predicate.values, sql);
|
|
147
|
+
return predicate.negated ? notTruth(truth) : truth;
|
|
148
|
+
}
|
|
149
|
+
case "between": {
|
|
150
|
+
const value = operandValue(predicate.operand, row, resolver);
|
|
151
|
+
const lower = operandValue(predicate.lower, row, resolver);
|
|
152
|
+
const upper = operandValue(predicate.upper, row, resolver);
|
|
153
|
+
if (value.kind === "empty" || lower.kind === "empty" || upper.kind === "empty") return "unknown";
|
|
154
|
+
const truth = andTruth(truthOfComparison(compareValues(value, lower, sql), ">="), truthOfComparison(compareValues(value, upper, sql), "<="));
|
|
155
|
+
return predicate.negated ? notTruth(truth) : truth;
|
|
156
|
+
}
|
|
157
|
+
case "not": return notTruth(evaluatePredicate(predicate.predicate, row, resolver, sql));
|
|
158
|
+
case "and": return andTruth(evaluatePredicate(predicate.left, row, resolver, sql), evaluatePredicate(predicate.right, row, resolver, sql));
|
|
159
|
+
case "or": return orTruth(evaluatePredicate(predicate.left, row, resolver, sql), evaluatePredicate(predicate.right, row, resolver, sql));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function aggregateOverValues(aggregate, values, sql) {
|
|
163
|
+
return aggregateCellValues(aggregate, values, sqlFailure(sql));
|
|
164
|
+
}
|
|
165
|
+
function compareForSort(left, right, sql) {
|
|
166
|
+
const leftNull = left.kind === "empty";
|
|
167
|
+
const rightNull = right.kind === "empty";
|
|
168
|
+
if (leftNull || rightNull) {
|
|
169
|
+
if (leftNull && rightNull) return 0;
|
|
170
|
+
return leftNull ? 1 : -1;
|
|
171
|
+
}
|
|
172
|
+
return compareValues(left, right, sql);
|
|
173
|
+
}
|
|
174
|
+
function sortRows(rows, directions, sql) {
|
|
175
|
+
return [...rows].sort((left, right) => {
|
|
176
|
+
for (const [index, direction] of directions.entries()) {
|
|
177
|
+
const leftKey = left.sortKeys[index];
|
|
178
|
+
const rightKey = right.sortKeys[index];
|
|
179
|
+
if (leftKey === void 0 || rightKey === void 0) throw new HsqldbSqlEvaluationError("an ORDER BY term has no sort key on this row", sql);
|
|
180
|
+
const ordering = compareForSort(leftKey, rightKey, sql);
|
|
181
|
+
if (ordering !== 0) return direction === "asc" ? ordering : -ordering;
|
|
182
|
+
}
|
|
183
|
+
return 0;
|
|
184
|
+
}).map((row) => row.output);
|
|
185
|
+
}
|
|
186
|
+
function groupKeyOf(values) {
|
|
187
|
+
return values.map((value) => {
|
|
188
|
+
switch (value.kind) {
|
|
189
|
+
case "empty": return "null";
|
|
190
|
+
case "boolean": return `boolean:${String(value.value)}`;
|
|
191
|
+
case "number":
|
|
192
|
+
case "percentage":
|
|
193
|
+
case "currency": return `numeric:${String(value.value)}`;
|
|
194
|
+
case "date":
|
|
195
|
+
case "time":
|
|
196
|
+
case "dateTime":
|
|
197
|
+
case "string":
|
|
198
|
+
case "error": return `text:${value.value}`;
|
|
199
|
+
}
|
|
200
|
+
}).join("\0");
|
|
201
|
+
}
|
|
202
|
+
function planSelectList(statement, table, resolver) {
|
|
203
|
+
const plan = [];
|
|
204
|
+
for (const item of statement.items) switch (item.kind) {
|
|
205
|
+
case "star":
|
|
206
|
+
for (const [index] of table.columns.entries()) plan.push({
|
|
207
|
+
kind: "column",
|
|
208
|
+
index,
|
|
209
|
+
text: resolver.nameAt(index)
|
|
210
|
+
});
|
|
211
|
+
break;
|
|
212
|
+
case "column":
|
|
213
|
+
plan.push({
|
|
214
|
+
kind: "column",
|
|
215
|
+
index: resolver.indexOf(item.column),
|
|
216
|
+
text: item.column.text
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
case "aggregate": plan.push({
|
|
220
|
+
kind: "aggregate",
|
|
221
|
+
aggregate: item.aggregate,
|
|
222
|
+
argumentIndex: item.argument.kind === "star" ? void 0 : resolver.indexOf(item.argument.column),
|
|
223
|
+
outputName: item.outputName
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return plan;
|
|
227
|
+
}
|
|
228
|
+
function planColumnNames(plan, resolver) {
|
|
229
|
+
return plan.map((item) => item.kind === "column" ? resolver.nameAt(item.index) : item.outputName);
|
|
230
|
+
}
|
|
231
|
+
function evaluateUngrouped(statement, plan, matching, resolver) {
|
|
232
|
+
const projection = plan.map((item) => {
|
|
233
|
+
if (item.kind !== "column") throw new HsqldbSqlEvaluationError(`${item.outputName} is an aggregate, so every other select-list item must be grouped`, statement.sql);
|
|
234
|
+
return item.index;
|
|
235
|
+
});
|
|
236
|
+
const orderIndices = statement.orderBy.map((term) => resolver.indexOf(term.column));
|
|
237
|
+
const sortable = matching.map((row) => ({
|
|
238
|
+
output: projection.map((index) => resolver.valueAt(index, row)),
|
|
239
|
+
sortKeys: orderIndices.map((index) => resolver.valueAt(index, row))
|
|
240
|
+
}));
|
|
241
|
+
return {
|
|
242
|
+
columns: planColumnNames(plan, resolver),
|
|
243
|
+
rows: sortRows(sortable, statement.orderBy.map((term) => term.direction), statement.sql)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function partitionIntoGroups(rows, groupIndices, resolver) {
|
|
247
|
+
const groups = /* @__PURE__ */ new Map();
|
|
248
|
+
for (const row of rows) {
|
|
249
|
+
const keyValues = groupIndices.map((index) => resolver.valueAt(index, row));
|
|
250
|
+
const key = groupKeyOf(keyValues);
|
|
251
|
+
const existing = groups.get(key);
|
|
252
|
+
if (existing === void 0) groups.set(key, {
|
|
253
|
+
keyValues,
|
|
254
|
+
rows: [row]
|
|
255
|
+
});
|
|
256
|
+
else existing.rows.push(row);
|
|
257
|
+
}
|
|
258
|
+
return [...groups.values()];
|
|
259
|
+
}
|
|
260
|
+
function evaluateGrouped(statement, plan, matching, resolver) {
|
|
261
|
+
const groupIndices = statement.groupBy.map((ref) => resolver.indexOf(ref));
|
|
262
|
+
for (const item of plan) if (item.kind === "column" && !groupIndices.includes(item.index)) throw new HsqldbSqlEvaluationError(`column "${item.text}" is neither grouped nor aggregated -- add it to GROUP BY or wrap it in an aggregate`, statement.sql);
|
|
263
|
+
const groups = groupIndices.length === 0 ? [{
|
|
264
|
+
keyValues: [],
|
|
265
|
+
rows: [...matching]
|
|
266
|
+
}] : partitionIntoGroups(matching, groupIndices, resolver);
|
|
267
|
+
const orderPositions = statement.orderBy.map((term) => {
|
|
268
|
+
const position = groupIndices.indexOf(resolver.indexOf(term.column));
|
|
269
|
+
if (position < 0) throw new HsqldbSqlEvaluationError(`ORDER BY column "${term.column.text}" is not a GROUP BY column -- a grouped result has no single value for it`, statement.sql);
|
|
270
|
+
return position;
|
|
271
|
+
});
|
|
272
|
+
const sortable = groups.map((group) => ({
|
|
273
|
+
output: plan.map((item) => {
|
|
274
|
+
if (item.kind === "aggregate") {
|
|
275
|
+
const argumentIndex = item.argumentIndex;
|
|
276
|
+
if (argumentIndex === void 0) return {
|
|
277
|
+
kind: "number",
|
|
278
|
+
value: group.rows.length
|
|
279
|
+
};
|
|
280
|
+
return aggregateOverValues(item.aggregate, group.rows.map((row) => resolver.valueAt(argumentIndex, row)), statement.sql);
|
|
281
|
+
}
|
|
282
|
+
const keyValue = group.keyValues[groupIndices.indexOf(item.index)];
|
|
283
|
+
if (keyValue === void 0) throw new HsqldbSqlEvaluationError(`grouped column "${item.text}" has no value on this group`, statement.sql);
|
|
284
|
+
return keyValue;
|
|
285
|
+
}),
|
|
286
|
+
sortKeys: orderPositions.map((position) => {
|
|
287
|
+
const keyValue = group.keyValues[position];
|
|
288
|
+
if (keyValue === void 0) throw new HsqldbSqlEvaluationError("an ORDER BY term resolved to a GROUP BY column with no value on this group", statement.sql);
|
|
289
|
+
return keyValue;
|
|
290
|
+
})
|
|
291
|
+
}));
|
|
292
|
+
return {
|
|
293
|
+
columns: planColumnNames(plan, resolver),
|
|
294
|
+
rows: sortRows(sortable, statement.orderBy.map((term) => term.direction), statement.sql)
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function evaluateSelect(statement, tables) {
|
|
298
|
+
const table = resolveTable(tables, statement.from, statement.sql);
|
|
299
|
+
const resolver = new ColumnResolver(table, statement.sql);
|
|
300
|
+
const plan = planSelectList(statement, table, resolver);
|
|
301
|
+
const where = statement.where;
|
|
302
|
+
const matching = where === void 0 ? table.rows : table.rows.filter((row) => evaluatePredicate(where, row, resolver, statement.sql) === "true");
|
|
303
|
+
const hasAggregate = plan.some((item) => item.kind === "aggregate");
|
|
304
|
+
if (statement.groupBy.length > 0 || hasAggregate) {
|
|
305
|
+
if (statement.groupBy.length > 0 && statement.items.some((item) => item.kind === "star")) throw new HsqldbSqlEvaluationError("SELECT * is not valid with GROUP BY -- name the grouped columns and the aggregates explicitly", statement.sql);
|
|
306
|
+
return evaluateGrouped(statement, plan, matching, resolver);
|
|
307
|
+
}
|
|
308
|
+
return evaluateUngrouped(statement, plan, matching, resolver);
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
311
|
+
export { evaluateSelect };
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_odb_sql_errors = require("./errors.cjs");
|
|
3
|
+
//#region src/odb/sql/lexer.ts
|
|
4
|
+
const KEYWORDS = /* @__PURE__ */ new Set([
|
|
5
|
+
"SELECT",
|
|
6
|
+
"FROM",
|
|
7
|
+
"WHERE",
|
|
8
|
+
"GROUP",
|
|
9
|
+
"BY",
|
|
10
|
+
"ORDER",
|
|
11
|
+
"ASC",
|
|
12
|
+
"DESC",
|
|
13
|
+
"AND",
|
|
14
|
+
"OR",
|
|
15
|
+
"NOT",
|
|
16
|
+
"IS",
|
|
17
|
+
"NULL",
|
|
18
|
+
"LIKE",
|
|
19
|
+
"IN",
|
|
20
|
+
"BETWEEN",
|
|
21
|
+
"TRUE",
|
|
22
|
+
"FALSE",
|
|
23
|
+
"COUNT",
|
|
24
|
+
"SUM",
|
|
25
|
+
"AVG",
|
|
26
|
+
"MIN",
|
|
27
|
+
"MAX",
|
|
28
|
+
"JOIN",
|
|
29
|
+
"INNER",
|
|
30
|
+
"OUTER",
|
|
31
|
+
"LEFT",
|
|
32
|
+
"RIGHT",
|
|
33
|
+
"FULL",
|
|
34
|
+
"CROSS",
|
|
35
|
+
"NATURAL",
|
|
36
|
+
"ON",
|
|
37
|
+
"USING",
|
|
38
|
+
"UNION",
|
|
39
|
+
"INTERSECT",
|
|
40
|
+
"EXCEPT",
|
|
41
|
+
"MINUS",
|
|
42
|
+
"DISTINCT",
|
|
43
|
+
"ALL",
|
|
44
|
+
"HAVING",
|
|
45
|
+
"LIMIT",
|
|
46
|
+
"OFFSET",
|
|
47
|
+
"FETCH",
|
|
48
|
+
"TOP",
|
|
49
|
+
"AS",
|
|
50
|
+
"CASE",
|
|
51
|
+
"WHEN",
|
|
52
|
+
"THEN",
|
|
53
|
+
"ELSE",
|
|
54
|
+
"END",
|
|
55
|
+
"EXISTS",
|
|
56
|
+
"ANY",
|
|
57
|
+
"SOME",
|
|
58
|
+
"WITH",
|
|
59
|
+
"INSERT",
|
|
60
|
+
"UPDATE",
|
|
61
|
+
"DELETE",
|
|
62
|
+
"CREATE",
|
|
63
|
+
"DROP",
|
|
64
|
+
"ALTER",
|
|
65
|
+
"INTO",
|
|
66
|
+
"VALUES",
|
|
67
|
+
"SET",
|
|
68
|
+
"ESCAPE",
|
|
69
|
+
"COLLATE",
|
|
70
|
+
"NULLS",
|
|
71
|
+
"FIRST",
|
|
72
|
+
"LAST"
|
|
73
|
+
]);
|
|
74
|
+
const UNSUPPORTED_SYMBOLS = [
|
|
75
|
+
["--", "a SQL line comment"],
|
|
76
|
+
["/*", "a SQL block comment"],
|
|
77
|
+
["||", "the || string-concatenation operator"],
|
|
78
|
+
["!=", "the != operator (write <> instead)"],
|
|
79
|
+
["+", "an arithmetic expression"],
|
|
80
|
+
["/", "an arithmetic expression"],
|
|
81
|
+
["%", "an arithmetic expression"],
|
|
82
|
+
["?", "a parameter placeholder"],
|
|
83
|
+
[":", "a named parameter"],
|
|
84
|
+
["@", "a variable reference"],
|
|
85
|
+
["#", "a variable reference"],
|
|
86
|
+
["&", "a bitwise operator"],
|
|
87
|
+
["|", "a bitwise operator"],
|
|
88
|
+
["^", "a bitwise operator"],
|
|
89
|
+
["~", "a bitwise operator"],
|
|
90
|
+
["!", "the ! operator"]
|
|
91
|
+
];
|
|
92
|
+
function isDigit(character) {
|
|
93
|
+
return character >= "0" && character <= "9";
|
|
94
|
+
}
|
|
95
|
+
function isIdentifierStart(character) {
|
|
96
|
+
return character >= "A" && character <= "Z" || character >= "a" && character <= "z" || character === "_";
|
|
97
|
+
}
|
|
98
|
+
function isIdentifierPart(character) {
|
|
99
|
+
return isIdentifierStart(character) || isDigit(character) || character === "$";
|
|
100
|
+
}
|
|
101
|
+
function isWhitespace(character) {
|
|
102
|
+
return character === " " || character === " " || character === "\n" || character === "\r" || character === "\f" || character === "\v";
|
|
103
|
+
}
|
|
104
|
+
function readNumber(sql, start) {
|
|
105
|
+
let cursor = start;
|
|
106
|
+
while (cursor < sql.length && isDigit(sql.charAt(cursor))) cursor += 1;
|
|
107
|
+
if (cursor < sql.length && sql.charAt(cursor) === ".") {
|
|
108
|
+
cursor += 1;
|
|
109
|
+
while (cursor < sql.length && isDigit(sql.charAt(cursor))) cursor += 1;
|
|
110
|
+
}
|
|
111
|
+
if (cursor < sql.length && (sql.charAt(cursor) === "e" || sql.charAt(cursor) === "E")) {
|
|
112
|
+
const exponentStart = cursor;
|
|
113
|
+
cursor += 1;
|
|
114
|
+
if (cursor < sql.length && (sql.charAt(cursor) === "+" || sql.charAt(cursor) === "-")) cursor += 1;
|
|
115
|
+
if (cursor < sql.length && isDigit(sql.charAt(cursor))) while (cursor < sql.length && isDigit(sql.charAt(cursor))) cursor += 1;
|
|
116
|
+
else cursor = exponentStart;
|
|
117
|
+
}
|
|
118
|
+
const text = sql.slice(start, cursor);
|
|
119
|
+
const value = Number(text);
|
|
120
|
+
if (!Number.isFinite(value)) throw new require_odb_sql_errors.HsqldbSqlParseError(`numeric literal "${text}" is not a finite number`, sql, start);
|
|
121
|
+
return {
|
|
122
|
+
value,
|
|
123
|
+
next: cursor
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function readStringLiteral(sql, start) {
|
|
127
|
+
let cursor = start + 1;
|
|
128
|
+
let value = "";
|
|
129
|
+
while (cursor < sql.length) {
|
|
130
|
+
const character = sql.charAt(cursor);
|
|
131
|
+
if (character === "'") {
|
|
132
|
+
if (sql.charAt(cursor + 1) === "'") {
|
|
133
|
+
value += "'";
|
|
134
|
+
cursor += 2;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
value,
|
|
139
|
+
next: cursor + 1
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
value += character;
|
|
143
|
+
cursor += 1;
|
|
144
|
+
}
|
|
145
|
+
throw new require_odb_sql_errors.HsqldbSqlParseError("unterminated string literal", sql, start);
|
|
146
|
+
}
|
|
147
|
+
function readQuotedIdentifier(sql, start) {
|
|
148
|
+
let cursor = start + 1;
|
|
149
|
+
let name = "";
|
|
150
|
+
while (cursor < sql.length) {
|
|
151
|
+
const character = sql.charAt(cursor);
|
|
152
|
+
if (character === "\"") {
|
|
153
|
+
if (sql.charAt(cursor + 1) === "\"") {
|
|
154
|
+
name += "\"";
|
|
155
|
+
cursor += 2;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (name === "") throw new require_odb_sql_errors.HsqldbSqlParseError("empty quoted identifier (\"\")", sql, start);
|
|
159
|
+
return {
|
|
160
|
+
name,
|
|
161
|
+
next: cursor + 1
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
name += character;
|
|
165
|
+
cursor += 1;
|
|
166
|
+
}
|
|
167
|
+
throw new require_odb_sql_errors.HsqldbSqlParseError("unterminated quoted identifier", sql, start);
|
|
168
|
+
}
|
|
169
|
+
function unsupportedSymbolAt(sql, cursor) {
|
|
170
|
+
for (const [symbol, construct] of UNSUPPORTED_SYMBOLS) if (sql.startsWith(symbol, cursor)) return construct;
|
|
171
|
+
}
|
|
172
|
+
function tokenizeSql(sql) {
|
|
173
|
+
const tokens = [];
|
|
174
|
+
let cursor = 0;
|
|
175
|
+
while (cursor < sql.length) {
|
|
176
|
+
const character = sql.charAt(cursor);
|
|
177
|
+
if (isWhitespace(character)) {
|
|
178
|
+
cursor += 1;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (character === "'") {
|
|
182
|
+
const start = cursor;
|
|
183
|
+
const literal = readStringLiteral(sql, cursor);
|
|
184
|
+
cursor = literal.next;
|
|
185
|
+
tokens.push({
|
|
186
|
+
kind: "string",
|
|
187
|
+
value: literal.value,
|
|
188
|
+
text: sql.slice(start, cursor),
|
|
189
|
+
start
|
|
190
|
+
});
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (character === "\"") {
|
|
194
|
+
const start = cursor;
|
|
195
|
+
const identifier = readQuotedIdentifier(sql, cursor);
|
|
196
|
+
cursor = identifier.next;
|
|
197
|
+
tokens.push({
|
|
198
|
+
kind: "identifier",
|
|
199
|
+
name: identifier.name,
|
|
200
|
+
quoted: true,
|
|
201
|
+
text: sql.slice(start, cursor),
|
|
202
|
+
start
|
|
203
|
+
});
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (isDigit(character) || character === "." && isDigit(sql.charAt(cursor + 1))) {
|
|
207
|
+
const start = cursor;
|
|
208
|
+
const numeric = readNumber(sql, cursor);
|
|
209
|
+
cursor = numeric.next;
|
|
210
|
+
tokens.push({
|
|
211
|
+
kind: "number",
|
|
212
|
+
value: numeric.value,
|
|
213
|
+
text: sql.slice(start, cursor),
|
|
214
|
+
start
|
|
215
|
+
});
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (isIdentifierStart(character)) {
|
|
219
|
+
const start = cursor;
|
|
220
|
+
while (cursor < sql.length && isIdentifierPart(sql.charAt(cursor))) cursor += 1;
|
|
221
|
+
const text = sql.slice(start, cursor);
|
|
222
|
+
const folded = text.toUpperCase();
|
|
223
|
+
if (KEYWORDS.has(folded)) tokens.push({
|
|
224
|
+
kind: "keyword",
|
|
225
|
+
keyword: folded,
|
|
226
|
+
text,
|
|
227
|
+
start
|
|
228
|
+
});
|
|
229
|
+
else tokens.push({
|
|
230
|
+
kind: "identifier",
|
|
231
|
+
name: folded,
|
|
232
|
+
quoted: false,
|
|
233
|
+
text,
|
|
234
|
+
start
|
|
235
|
+
});
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (character === "<" || character === ">" || character === "=") {
|
|
239
|
+
const start = cursor;
|
|
240
|
+
const twoCharacter = sql.slice(cursor, cursor + 2);
|
|
241
|
+
if (twoCharacter === "<=" || twoCharacter === ">=" || twoCharacter === "<>") {
|
|
242
|
+
cursor += 2;
|
|
243
|
+
tokens.push({
|
|
244
|
+
kind: "operator",
|
|
245
|
+
operator: twoCharacter,
|
|
246
|
+
text: twoCharacter,
|
|
247
|
+
start
|
|
248
|
+
});
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
cursor += 1;
|
|
252
|
+
tokens.push({
|
|
253
|
+
kind: "operator",
|
|
254
|
+
operator: character,
|
|
255
|
+
text: character,
|
|
256
|
+
start
|
|
257
|
+
});
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const unsupported = unsupportedSymbolAt(sql, cursor);
|
|
261
|
+
if (unsupported !== void 0) throw new require_odb_sql_errors.HsqldbSqlUnsupportedError(unsupported, sql);
|
|
262
|
+
if (character === "(" || character === ")" || character === "," || character === "." || character === "*" || character === ";" || character === "-") {
|
|
263
|
+
tokens.push({
|
|
264
|
+
kind: "punctuation",
|
|
265
|
+
punctuation: character,
|
|
266
|
+
text: character,
|
|
267
|
+
start: cursor
|
|
268
|
+
});
|
|
269
|
+
cursor += 1;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
throw new require_odb_sql_errors.HsqldbSqlParseError(`unexpected character "${character}"`, sql, cursor);
|
|
273
|
+
}
|
|
274
|
+
tokens.push({
|
|
275
|
+
kind: "end",
|
|
276
|
+
text: "",
|
|
277
|
+
start: sql.length
|
|
278
|
+
});
|
|
279
|
+
return tokens;
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
exports.tokenizeSql = tokenizeSql;
|