js.documents 7.8.21 → 7.9.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 +2 -2
- package/dist/{evaluate-DjUkdPfO.d.cts → evaluate-3T-0yAut.d.cts} +1 -1
- package/dist/{evaluate-BKLK67uP.d.ts → evaluate-Ddsxehrf.d.ts} +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/odb/formula/evaluate.d.cts +1 -1
- package/dist/odb/formula/evaluate.d.ts +1 -1
- package/dist/odb/report/render.d.cts +1 -1
- package/dist/odb/report/render.d.ts +1 -1
- package/dist/odb/report/source.d.cts +1 -1
- package/dist/odb/report/source.d.ts +1 -1
- package/dist/odb/sql/evaluate.cjs +136 -33
- package/dist/odb/sql/evaluate.d.cts +1 -1
- package/dist/odb/sql/evaluate.d.ts +1 -1
- package/dist/odb/sql/evaluate.js +136 -33
- package/dist/odb/sql/parser.cjs +85 -24
- package/dist/odb/sql/parser.d.cts +2 -2
- package/dist/odb/sql/parser.d.ts +2 -2
- package/dist/odb/sql/parser.js +85 -24
- package/dist/{parser-fyVOtBAo.d.ts → parser-D-9P_0Qd.d.ts} +17 -2
- package/dist/{parser-D9fiOUfR.d.cts → parser-h1n0BflA.d.cts} +17 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -606,7 +606,7 @@ import { readFirebirdBackup } from "documents.js";
|
|
|
606
606
|
const { summary, tables } = readFirebirdBackup(firebirdBackupBytes);
|
|
607
607
|
```
|
|
608
608
|
|
|
609
|
-
**SQL `SELECT` engine** — `parseSelect`/`evaluateSelect` run a bounded `SELECT` over `readOdbTables`' output. Closed allowlist grammar: column list or `*` or aggregates (`COUNT`/`SUM`/`AVG`/`MIN`/`MAX`), `FROM` one table plus zero or more `[INNER]
|
|
609
|
+
**SQL `SELECT` engine** — `parseSelect`/`evaluateSelect` run a bounded `SELECT` over `readOdbTables`' output. Closed allowlist grammar: column list or `*` or aggregates (`COUNT`/`SUM`/`AVG`/`MIN`/`MAX`), `FROM` one table plus zero or more joins (`[INNER]`, `LEFT [OUTER]`, `RIGHT [OUTER]`, `FULL [OUTER]`, `CROSS`, or `NATURAL`, each a plain nested-loop join with `ON`, `USING (col, ...)`, or NATURAL's own implicit equi-join over shared columns; a `USING`/`NATURAL` join's shared columns merge into one output column, `COALESCE(left, right)`), optional table aliases (`FROM t [AS] alias`, which is what makes a genuine self-join possible), optional `WHERE`/`GROUP BY`/`ORDER BY`. Subqueries, column aliases, and everything else outside the grammar throws `HsqldbSqlUnsupportedError`:
|
|
610
610
|
|
|
611
611
|
```ts
|
|
612
612
|
import { decodePackage, readOdbInventory } from "odf.js";
|
|
@@ -892,7 +892,7 @@ To run a single test file: `pnpm vitest run src/path/to/file.test.ts`.
|
|
|
892
892
|
- **The rpt formula engine's group scoping cascades enclosing breaks inward.** A group at level L starts a new instance when its own expression breaks OR when any enclosing group breaks — otherwise a "Q2" subtotal would span two regions. `HASCHANGED` itself knows nothing about groups; the cascade lives in the report structure. Aggregates are computed over complete ranges (not running totals); group expressions may not transitively depend on aggregates (circular).
|
|
893
893
|
- **The rpt function set is a closed allowlist; separator is semicolon.** `rpt:HASCHANGED`/`rpt:LEFT`/`rpt:SUM`/`COUNT`/`AVG`/`MIN`/`MAX`/`field:[COLUMN]` — everything else throws. `[NAME]` and `"NAME"` are one concept. Three refusals where guessing would produce wrong values: non-boolean group expressions, `rpt:LEFT` over non-text, per-row formulas in report header/footer.
|
|
894
894
|
- **The rpt engine emits no page headers/footers** — the renderer places them under a single-logical-page model, at report scope.
|
|
895
|
-
- **The SQL engine is a closed allowlist** —
|
|
895
|
+
- **The SQL engine is a closed allowlist** — subqueries, `UNION`, `DISTINCT`, `HAVING`, `LIMIT`, column aliases, `CASE`, arithmetic, etc. all throw `HsqldbSqlUnsupportedError` naming the construct. Silently dropping a clause would return plausible wrong rows. JOINs (every kind, `USING`, `NATURAL`) and table aliases are real grammar.
|
|
896
896
|
- **Four SQL semantics decisions:** (1) NULL is `{ kind: 'empty' }`, three-valued logic; (2) values compare within classes (numeric/boolean/text), cross-class throws; (3) `GROUP BY` puts NULLs in one group, first-appearance order; `COUNT(*)` counts rows, `COUNT(column)` counts non-NULL; (4) `ORDER BY` sorts NULLs last under ASC, stable.
|
|
897
897
|
- **Unquoted SQL identifiers fold to upper case; double-quoted match exactly.**
|
|
898
898
|
- **All four `.odb` decoder tiers are implemented.** Tier 4 (BINARY/COMPRESSED) is a sibling of Tier 2, not a new value decoder — it recovers DDL as TEXT-format script text and decodes rows through the same per-column encoder. An external-only connection is a permanent scope boundary.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as HsqldbTable } from "./script-DsGGIzGU.cjs";
|
|
2
|
-
import {
|
|
2
|
+
import { m as SqlSelectStatement } from "./parser-h1n0BflA.cjs";
|
|
3
3
|
import { ContentCellValue } from "document-schema.js";
|
|
4
4
|
//#region src/odb/sql/evaluate.d.ts
|
|
5
5
|
interface SqlResultSet {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as HsqldbTable } from "./script-DsGGIzGU.js";
|
|
2
|
-
import {
|
|
2
|
+
import { m as SqlSelectStatement } from "./parser-D-9P_0Qd.js";
|
|
3
3
|
import { ContentCellValue } from "document-schema.js";
|
|
4
4
|
//#region src/odb/sql/evaluate.d.ts
|
|
5
5
|
interface SqlResultSet {
|
package/dist/index.d.cts
CHANGED
|
@@ -114,8 +114,8 @@ import { odbTablesToSpreadsheetDocument } from "./odb/spreadsheet.cjs";
|
|
|
114
114
|
import { readOdbForms, readOdbReports } from "./odb/components.cjs";
|
|
115
115
|
import { OdbTableNotFoundError, OdbTableNotSpecifiedError } from "./odb/csv.cjs";
|
|
116
116
|
import { i as tokenizeSql, n as SqlPunctuation, r as SqlToken, t as SqlComparisonOperator } from "./lexer-DgsW7jMA.cjs";
|
|
117
|
-
import { a as SqlJoinClause, c as
|
|
118
|
-
import { n as evaluateSelect, t as SqlResultSet } from "./evaluate-
|
|
117
|
+
import { a as SqlJoinClause, c as SqlLiteral, d as SqlOrderByTerm, f as SqlPredicate, g as parseSelect, h as SqlSortDirection, i as SqlFromClause, l as SqlNameRef, m as SqlSelectStatement, n as SqlAggregateFunction, p as SqlSelectItem, r as SqlColumnRef, t as SqlAggregateArgument, u as SqlOperand } from "./parser-h1n0BflA.cjs";
|
|
118
|
+
import { n as evaluateSelect, t as SqlResultSet } from "./evaluate-3T-0yAut.cjs";
|
|
119
119
|
import { HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError } from "./odb/sql/errors.cjs";
|
|
120
120
|
import { RptBandDefinition, RptBandInstance, RptBandKind, RptGroupDefinition, RptNamedFunctionDefinition, RptReportDefinition, RptReportRun, RptScope, evaluateRptBandOutsideData, runRptReport } from "./odb/formula/evaluate.cjs";
|
|
121
121
|
import { RptAggregateFunction, RptFormula, RptReference, parseRptFormula } from "./odb/formula/parser.cjs";
|
package/dist/index.d.ts
CHANGED
|
@@ -114,8 +114,8 @@ import { odbTablesToSpreadsheetDocument } from "./odb/spreadsheet.js";
|
|
|
114
114
|
import { readOdbForms, readOdbReports } from "./odb/components.js";
|
|
115
115
|
import { OdbTableNotFoundError, OdbTableNotSpecifiedError } from "./odb/csv.js";
|
|
116
116
|
import { i as tokenizeSql, n as SqlPunctuation, r as SqlToken, t as SqlComparisonOperator } from "./lexer-DgsW7jMA.js";
|
|
117
|
-
import { a as SqlJoinClause, c as
|
|
118
|
-
import { n as evaluateSelect, t as SqlResultSet } from "./evaluate-
|
|
117
|
+
import { a as SqlJoinClause, c as SqlLiteral, d as SqlOrderByTerm, f as SqlPredicate, g as parseSelect, h as SqlSortDirection, i as SqlFromClause, l as SqlNameRef, m as SqlSelectStatement, n as SqlAggregateFunction, p as SqlSelectItem, r as SqlColumnRef, t as SqlAggregateArgument, u as SqlOperand } from "./parser-D-9P_0Qd.js";
|
|
118
|
+
import { n as evaluateSelect, t as SqlResultSet } from "./evaluate-Ddsxehrf.js";
|
|
119
119
|
import { HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError } from "./odb/sql/errors.js";
|
|
120
120
|
import { RptBandDefinition, RptBandInstance, RptBandKind, RptGroupDefinition, RptNamedFunctionDefinition, RptReportDefinition, RptReportRun, RptScope, evaluateRptBandOutsideData, runRptReport } from "./odb/formula/evaluate.js";
|
|
121
121
|
import { RptAggregateFunction, RptFormula, RptReference, parseRptFormula } from "./odb/formula/parser.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as SqlResultSet } from "../../evaluate-
|
|
1
|
+
import { t as SqlResultSet } from "../../evaluate-3T-0yAut.cjs";
|
|
2
2
|
import { ContentCellValue } from "document-schema.js";
|
|
3
3
|
//#region src/odb/formula/evaluate.d.ts
|
|
4
4
|
type RptBandKind = "report-header" | "group-header" | "detail" | "group-footer" | "report-footer";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as SqlResultSet } from "../../evaluate-
|
|
1
|
+
import { t as SqlResultSet } from "../../evaluate-Ddsxehrf.js";
|
|
2
2
|
import { ContentCellValue } from "document-schema.js";
|
|
3
3
|
//#region src/odb/formula/evaluate.d.ts
|
|
4
4
|
type RptBandKind = "report-header" | "group-header" | "detail" | "group-footer" | "report-footer";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as HsqldbDecodeOptions } from "../../rowformat-Cl2exlEh.cjs";
|
|
2
|
-
import { t as SqlResultSet } from "../../evaluate-
|
|
2
|
+
import { t as SqlResultSet } from "../../evaluate-3T-0yAut.cjs";
|
|
3
3
|
import { OdbQueryInfo, OdbReport, Package } from "odf.js";
|
|
4
4
|
//#region src/odb/report/source.d.ts
|
|
5
5
|
declare class OdbReportDataSourceError extends Error {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as HsqldbDecodeOptions } from "../../rowformat-Cl2exlEh.js";
|
|
2
|
-
import { t as SqlResultSet } from "../../evaluate-
|
|
2
|
+
import { t as SqlResultSet } from "../../evaluate-Ddsxehrf.js";
|
|
3
3
|
import { OdbQueryInfo, OdbReport, Package } from "odf.js";
|
|
4
4
|
//#region src/odb/report/source.d.ts
|
|
5
5
|
declare class OdbReportDataSourceError extends Error {
|
|
@@ -71,6 +71,16 @@ function resolveTable(tables, ref, sql) {
|
|
|
71
71
|
if (table === void 0) throw new require_odb_sql_errors.HsqldbSqlEvaluationError(`table "${resolvedName}" not found`, sql);
|
|
72
72
|
return table;
|
|
73
73
|
}
|
|
74
|
+
function columnAt(columns, index, sql) {
|
|
75
|
+
const column = columns[index];
|
|
76
|
+
if (column === void 0) throw new require_odb_sql_errors.HsqldbSqlEvaluationError(`column index ${String(index)} is outside the joined column list`, sql);
|
|
77
|
+
return column;
|
|
78
|
+
}
|
|
79
|
+
function valueAtIndex(row, index, sql) {
|
|
80
|
+
const value = row[index];
|
|
81
|
+
if (value === void 0) throw new require_odb_sql_errors.HsqldbSqlEvaluationError(`malformed joined row: it carries ${String(row.length)} values but index ${String(index)} was expected`, sql);
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
74
84
|
function resolveColumnIndex(columns, candidateIndices, ref, sql) {
|
|
75
85
|
let firstExact;
|
|
76
86
|
let exactCount = 0;
|
|
@@ -98,39 +108,32 @@ var ColumnResolver = class {
|
|
|
98
108
|
sql;
|
|
99
109
|
cache = /* @__PURE__ */ new Map();
|
|
100
110
|
columns;
|
|
101
|
-
|
|
102
|
-
constructor(
|
|
111
|
+
qualifierNames;
|
|
112
|
+
constructor(columns, sql) {
|
|
103
113
|
this.sql = sql;
|
|
104
|
-
this.
|
|
105
|
-
this.
|
|
106
|
-
tableName: table.tableName,
|
|
107
|
-
columnName: column.name
|
|
108
|
-
})));
|
|
114
|
+
this.columns = columns;
|
|
115
|
+
this.qualifierNames = [...new Set(columns.flatMap((column) => column.qualifiers))];
|
|
109
116
|
}
|
|
110
117
|
get columnCount() {
|
|
111
118
|
return this.columns.length;
|
|
112
119
|
}
|
|
113
120
|
nameAt(index) {
|
|
114
|
-
|
|
115
|
-
if (column === void 0) throw new require_odb_sql_errors.HsqldbSqlEvaluationError(`column index ${String(index)} is outside the joined column list`, this.sql);
|
|
116
|
-
return column.columnName;
|
|
121
|
+
return columnAt(this.columns, index, this.sql).columnName;
|
|
117
122
|
}
|
|
118
123
|
indexOf(ref) {
|
|
119
124
|
const cached = this.cache.get(ref);
|
|
120
125
|
if (cached !== void 0) return cached;
|
|
121
126
|
let candidateIndices;
|
|
122
127
|
if (ref.qualifier !== void 0) {
|
|
123
|
-
const qualifierName = resolveName(this.
|
|
124
|
-
candidateIndices = this.columns.map((column, index) => column.
|
|
128
|
+
const qualifierName = resolveName(this.qualifierNames, ref.qualifier, "table qualifier", this.sql);
|
|
129
|
+
candidateIndices = this.columns.map((column, index) => column.qualifiers.includes(qualifierName) ? index : -1).filter((index) => index >= 0);
|
|
125
130
|
} else candidateIndices = this.columns.map((_column, index) => index);
|
|
126
131
|
const index = resolveColumnIndex(this.columns, candidateIndices, ref.column, this.sql);
|
|
127
132
|
this.cache.set(ref, index);
|
|
128
133
|
return index;
|
|
129
134
|
}
|
|
130
135
|
valueAt(index, row) {
|
|
131
|
-
|
|
132
|
-
if (value === void 0) throw new require_odb_sql_errors.HsqldbSqlEvaluationError(`malformed joined row: it carries ${String(row.length)} values but the joined column list declares ${String(this.columns.length)}`, this.sql);
|
|
133
|
-
return value;
|
|
136
|
+
return valueAtIndex(row, index, this.sql);
|
|
134
137
|
}
|
|
135
138
|
valueOf(ref, row) {
|
|
136
139
|
return this.valueAt(this.indexOf(ref), row);
|
|
@@ -326,29 +329,129 @@ function evaluateGrouped(statement, plan, matching, resolver) {
|
|
|
326
329
|
rows: sortRows(sortable, statement.orderBy.map((term) => term.direction), statement.sql)
|
|
327
330
|
};
|
|
328
331
|
}
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
332
|
+
function tableColumns(qualifier, table) {
|
|
333
|
+
return table.columns.map((column) => ({
|
|
334
|
+
qualifiers: [qualifier],
|
|
335
|
+
columnName: column.name
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
function nullRow(count) {
|
|
339
|
+
return new Array(count).fill(require_odb_values.CELL_NULL);
|
|
340
|
+
}
|
|
341
|
+
function computeNaturalSharedPairs(leftColumns, rightColumns, sql) {
|
|
342
|
+
const rightIndexByName = /* @__PURE__ */ new Map();
|
|
343
|
+
const ambiguousRightNames = /* @__PURE__ */ new Set();
|
|
344
|
+
rightColumns.forEach((column, index) => {
|
|
345
|
+
if (rightIndexByName.has(column.columnName)) ambiguousRightNames.add(column.columnName);
|
|
346
|
+
else rightIndexByName.set(column.columnName, index);
|
|
347
|
+
});
|
|
348
|
+
const pairs = [];
|
|
349
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
350
|
+
leftColumns.forEach((leftColumn, leftIndex) => {
|
|
351
|
+
const rightIndex = rightIndexByName.get(leftColumn.columnName);
|
|
352
|
+
if (rightIndex === void 0) return;
|
|
353
|
+
if (ambiguousRightNames.has(leftColumn.columnName) || seenNames.has(leftColumn.columnName)) throw new require_odb_sql_errors.HsqldbSqlEvaluationError(`NATURAL JOIN cannot determine a unique match for column "${leftColumn.columnName}" -- more than one column shares that name on one side of the join`, sql);
|
|
354
|
+
seenNames.add(leftColumn.columnName);
|
|
355
|
+
pairs.push({
|
|
356
|
+
leftIndex,
|
|
357
|
+
rightIndex,
|
|
358
|
+
columnName: leftColumn.columnName
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
return pairs;
|
|
362
|
+
}
|
|
363
|
+
function evaluateSharedColumnMatch(combined, pairs, leftColumnCount, sql) {
|
|
364
|
+
return pairs.every((pair) => {
|
|
365
|
+
const left = valueAtIndex(combined, pair.leftIndex, sql);
|
|
366
|
+
const right = valueAtIndex(combined, leftColumnCount + pair.rightIndex, sql);
|
|
367
|
+
return left.kind !== "empty" && right.kind !== "empty" && compareValues(left, right, sql) === 0;
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function mergeSharedColumns(rawColumns, rows, sharedPairs, leftColumnCount, sql) {
|
|
371
|
+
const sharedLeftIndices = new Set(sharedPairs.map((pair) => pair.leftIndex));
|
|
372
|
+
const sharedRightIndices = new Set(sharedPairs.map((pair) => leftColumnCount + pair.rightIndex));
|
|
373
|
+
const remainingIndices = rawColumns.map((_column, index) => index).filter((index) => !sharedLeftIndices.has(index) && !sharedRightIndices.has(index));
|
|
374
|
+
return {
|
|
375
|
+
columns: [...sharedPairs.map((pair) => {
|
|
376
|
+
const leftColumn = columnAt(rawColumns, pair.leftIndex, sql);
|
|
377
|
+
const rightColumn = columnAt(rawColumns, leftColumnCount + pair.rightIndex, sql);
|
|
378
|
+
return {
|
|
379
|
+
qualifiers: [.../* @__PURE__ */ new Set([...leftColumn.qualifiers, ...rightColumn.qualifiers])],
|
|
380
|
+
columnName: leftColumn.columnName
|
|
381
|
+
};
|
|
382
|
+
}), ...remainingIndices.map((index) => columnAt(rawColumns, index, sql))],
|
|
383
|
+
rows: rows.map((row) => {
|
|
384
|
+
const mergedValues = sharedPairs.map((pair) => {
|
|
385
|
+
const leftValue = valueAtIndex(row, pair.leftIndex, sql);
|
|
386
|
+
const rightValue = valueAtIndex(row, leftColumnCount + pair.rightIndex, sql);
|
|
387
|
+
return leftValue.kind !== "empty" ? leftValue : rightValue;
|
|
388
|
+
});
|
|
389
|
+
const remainingValues = remainingIndices.map((index) => valueAtIndex(row, index, sql));
|
|
390
|
+
return [...mergedValues, ...remainingValues];
|
|
391
|
+
})
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function applyJoinClause(left, join, tables, sql) {
|
|
395
|
+
const rightTable = resolveTable(tables, join.table, sql);
|
|
396
|
+
const rightColumns = tableColumns(join.alias !== void 0 ? join.alias.name : rightTable.tableName, rightTable);
|
|
397
|
+
const leftColumnCount = left.columns.length;
|
|
398
|
+
const rawColumns = [...left.columns, ...rightColumns];
|
|
399
|
+
let onPredicate;
|
|
400
|
+
let sharedPairs = [];
|
|
401
|
+
if (join.condition.kind === "on") onPredicate = join.condition.predicate;
|
|
402
|
+
else if (join.condition.kind === "using") {
|
|
403
|
+
const allLeftIndices = left.columns.map((_column, index) => index);
|
|
404
|
+
const allRightIndices = rightColumns.map((_column, index) => index);
|
|
405
|
+
sharedPairs = join.condition.columns.map((ref) => {
|
|
406
|
+
const leftIndex = resolveColumnIndex(left.columns, allLeftIndices, ref, sql);
|
|
407
|
+
return {
|
|
408
|
+
leftIndex,
|
|
409
|
+
rightIndex: resolveColumnIndex(rightColumns, allRightIndices, ref, sql),
|
|
410
|
+
columnName: columnAt(left.columns, leftIndex, sql).columnName
|
|
411
|
+
};
|
|
412
|
+
});
|
|
413
|
+
} else if (join.condition.kind === "natural") {
|
|
414
|
+
sharedPairs = computeNaturalSharedPairs(left.columns, rightColumns, sql);
|
|
415
|
+
if (sharedPairs.length === 0) throw new require_odb_sql_errors.HsqldbSqlEvaluationError("NATURAL JOIN found no columns shared between the joined tables", sql);
|
|
343
416
|
}
|
|
417
|
+
const rawResolver = new ColumnResolver(rawColumns, sql);
|
|
418
|
+
const matchedLeft = /* @__PURE__ */ new Set();
|
|
419
|
+
const matchedRight = /* @__PURE__ */ new Set();
|
|
420
|
+
const paired = [];
|
|
421
|
+
left.rows.forEach((leftRow, leftIndex) => {
|
|
422
|
+
rightTable.rows.forEach((rightRow, rightIndex) => {
|
|
423
|
+
const combined = [...leftRow, ...rightRow];
|
|
424
|
+
if (onPredicate !== void 0 ? evaluatePredicate(onPredicate, combined, rawResolver, sql) === "true" : sharedPairs.length > 0 ? evaluateSharedColumnMatch(combined, sharedPairs, leftColumnCount, sql) : true) {
|
|
425
|
+
paired.push(combined);
|
|
426
|
+
matchedLeft.add(leftIndex);
|
|
427
|
+
matchedRight.add(rightIndex);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
if (join.joinKind === "left" || join.joinKind === "full") left.rows.forEach((leftRow, leftIndex) => {
|
|
432
|
+
if (!matchedLeft.has(leftIndex)) paired.push([...leftRow, ...nullRow(rightColumns.length)]);
|
|
433
|
+
});
|
|
434
|
+
if (join.joinKind === "right" || join.joinKind === "full") rightTable.rows.forEach((rightRow, rightIndex) => {
|
|
435
|
+
if (!matchedRight.has(rightIndex)) paired.push([...nullRow(leftColumnCount), ...rightRow]);
|
|
436
|
+
});
|
|
437
|
+
if (join.condition.kind === "using" || join.condition.kind === "natural") return mergeSharedColumns(rawColumns, paired, sharedPairs, leftColumnCount, sql);
|
|
344
438
|
return {
|
|
345
|
-
|
|
346
|
-
rows
|
|
439
|
+
columns: rawColumns,
|
|
440
|
+
rows: paired
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function joinTables(from, tables, sql) {
|
|
444
|
+
const baseTable = resolveTable(tables, from.table, sql);
|
|
445
|
+
let result = {
|
|
446
|
+
columns: tableColumns(from.alias !== void 0 ? from.alias.name : baseTable.tableName, baseTable),
|
|
447
|
+
rows: baseTable.rows
|
|
347
448
|
};
|
|
449
|
+
for (const join of from.joins) result = applyJoinClause(result, join, tables, sql);
|
|
450
|
+
return result;
|
|
348
451
|
}
|
|
349
452
|
function evaluateSelect(statement, tables) {
|
|
350
|
-
const {
|
|
351
|
-
const resolver = new ColumnResolver(
|
|
453
|
+
const { columns, rows } = joinTables(statement.from, tables, statement.sql);
|
|
454
|
+
const resolver = new ColumnResolver(columns, statement.sql);
|
|
352
455
|
const plan = planSelectList(statement, resolver);
|
|
353
456
|
const where = statement.where;
|
|
354
457
|
const matching = where === void 0 ? rows : rows.filter((row) => evaluatePredicate(where, row, resolver, statement.sql) === "true");
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as evaluateSelect, t as SqlResultSet } from "../../evaluate-
|
|
1
|
+
import { n as evaluateSelect, t as SqlResultSet } from "../../evaluate-3T-0yAut.cjs";
|
|
2
2
|
export { SqlResultSet, evaluateSelect };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as evaluateSelect, t as SqlResultSet } from "../../evaluate-
|
|
1
|
+
import { n as evaluateSelect, t as SqlResultSet } from "../../evaluate-Ddsxehrf.js";
|
|
2
2
|
export { SqlResultSet, evaluateSelect };
|
package/dist/odb/sql/evaluate.js
CHANGED
|
@@ -70,6 +70,16 @@ function resolveTable(tables, ref, sql) {
|
|
|
70
70
|
if (table === void 0) throw new HsqldbSqlEvaluationError(`table "${resolvedName}" not found`, sql);
|
|
71
71
|
return table;
|
|
72
72
|
}
|
|
73
|
+
function columnAt(columns, index, sql) {
|
|
74
|
+
const column = columns[index];
|
|
75
|
+
if (column === void 0) throw new HsqldbSqlEvaluationError(`column index ${String(index)} is outside the joined column list`, sql);
|
|
76
|
+
return column;
|
|
77
|
+
}
|
|
78
|
+
function valueAtIndex(row, index, sql) {
|
|
79
|
+
const value = row[index];
|
|
80
|
+
if (value === void 0) throw new HsqldbSqlEvaluationError(`malformed joined row: it carries ${String(row.length)} values but index ${String(index)} was expected`, sql);
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
73
83
|
function resolveColumnIndex(columns, candidateIndices, ref, sql) {
|
|
74
84
|
let firstExact;
|
|
75
85
|
let exactCount = 0;
|
|
@@ -97,39 +107,32 @@ var ColumnResolver = class {
|
|
|
97
107
|
sql;
|
|
98
108
|
cache = /* @__PURE__ */ new Map();
|
|
99
109
|
columns;
|
|
100
|
-
|
|
101
|
-
constructor(
|
|
110
|
+
qualifierNames;
|
|
111
|
+
constructor(columns, sql) {
|
|
102
112
|
this.sql = sql;
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
105
|
-
tableName: table.tableName,
|
|
106
|
-
columnName: column.name
|
|
107
|
-
})));
|
|
113
|
+
this.columns = columns;
|
|
114
|
+
this.qualifierNames = [...new Set(columns.flatMap((column) => column.qualifiers))];
|
|
108
115
|
}
|
|
109
116
|
get columnCount() {
|
|
110
117
|
return this.columns.length;
|
|
111
118
|
}
|
|
112
119
|
nameAt(index) {
|
|
113
|
-
|
|
114
|
-
if (column === void 0) throw new HsqldbSqlEvaluationError(`column index ${String(index)} is outside the joined column list`, this.sql);
|
|
115
|
-
return column.columnName;
|
|
120
|
+
return columnAt(this.columns, index, this.sql).columnName;
|
|
116
121
|
}
|
|
117
122
|
indexOf(ref) {
|
|
118
123
|
const cached = this.cache.get(ref);
|
|
119
124
|
if (cached !== void 0) return cached;
|
|
120
125
|
let candidateIndices;
|
|
121
126
|
if (ref.qualifier !== void 0) {
|
|
122
|
-
const qualifierName = resolveName(this.
|
|
123
|
-
candidateIndices = this.columns.map((column, index) => column.
|
|
127
|
+
const qualifierName = resolveName(this.qualifierNames, ref.qualifier, "table qualifier", this.sql);
|
|
128
|
+
candidateIndices = this.columns.map((column, index) => column.qualifiers.includes(qualifierName) ? index : -1).filter((index) => index >= 0);
|
|
124
129
|
} else candidateIndices = this.columns.map((_column, index) => index);
|
|
125
130
|
const index = resolveColumnIndex(this.columns, candidateIndices, ref.column, this.sql);
|
|
126
131
|
this.cache.set(ref, index);
|
|
127
132
|
return index;
|
|
128
133
|
}
|
|
129
134
|
valueAt(index, row) {
|
|
130
|
-
|
|
131
|
-
if (value === void 0) throw new HsqldbSqlEvaluationError(`malformed joined row: it carries ${String(row.length)} values but the joined column list declares ${String(this.columns.length)}`, this.sql);
|
|
132
|
-
return value;
|
|
135
|
+
return valueAtIndex(row, index, this.sql);
|
|
133
136
|
}
|
|
134
137
|
valueOf(ref, row) {
|
|
135
138
|
return this.valueAt(this.indexOf(ref), row);
|
|
@@ -325,29 +328,129 @@ function evaluateGrouped(statement, plan, matching, resolver) {
|
|
|
325
328
|
rows: sortRows(sortable, statement.orderBy.map((term) => term.direction), statement.sql)
|
|
326
329
|
};
|
|
327
330
|
}
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
331
|
+
function tableColumns(qualifier, table) {
|
|
332
|
+
return table.columns.map((column) => ({
|
|
333
|
+
qualifiers: [qualifier],
|
|
334
|
+
columnName: column.name
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
337
|
+
function nullRow(count) {
|
|
338
|
+
return new Array(count).fill(CELL_NULL);
|
|
339
|
+
}
|
|
340
|
+
function computeNaturalSharedPairs(leftColumns, rightColumns, sql) {
|
|
341
|
+
const rightIndexByName = /* @__PURE__ */ new Map();
|
|
342
|
+
const ambiguousRightNames = /* @__PURE__ */ new Set();
|
|
343
|
+
rightColumns.forEach((column, index) => {
|
|
344
|
+
if (rightIndexByName.has(column.columnName)) ambiguousRightNames.add(column.columnName);
|
|
345
|
+
else rightIndexByName.set(column.columnName, index);
|
|
346
|
+
});
|
|
347
|
+
const pairs = [];
|
|
348
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
349
|
+
leftColumns.forEach((leftColumn, leftIndex) => {
|
|
350
|
+
const rightIndex = rightIndexByName.get(leftColumn.columnName);
|
|
351
|
+
if (rightIndex === void 0) return;
|
|
352
|
+
if (ambiguousRightNames.has(leftColumn.columnName) || seenNames.has(leftColumn.columnName)) throw new HsqldbSqlEvaluationError(`NATURAL JOIN cannot determine a unique match for column "${leftColumn.columnName}" -- more than one column shares that name on one side of the join`, sql);
|
|
353
|
+
seenNames.add(leftColumn.columnName);
|
|
354
|
+
pairs.push({
|
|
355
|
+
leftIndex,
|
|
356
|
+
rightIndex,
|
|
357
|
+
columnName: leftColumn.columnName
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
return pairs;
|
|
361
|
+
}
|
|
362
|
+
function evaluateSharedColumnMatch(combined, pairs, leftColumnCount, sql) {
|
|
363
|
+
return pairs.every((pair) => {
|
|
364
|
+
const left = valueAtIndex(combined, pair.leftIndex, sql);
|
|
365
|
+
const right = valueAtIndex(combined, leftColumnCount + pair.rightIndex, sql);
|
|
366
|
+
return left.kind !== "empty" && right.kind !== "empty" && compareValues(left, right, sql) === 0;
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
function mergeSharedColumns(rawColumns, rows, sharedPairs, leftColumnCount, sql) {
|
|
370
|
+
const sharedLeftIndices = new Set(sharedPairs.map((pair) => pair.leftIndex));
|
|
371
|
+
const sharedRightIndices = new Set(sharedPairs.map((pair) => leftColumnCount + pair.rightIndex));
|
|
372
|
+
const remainingIndices = rawColumns.map((_column, index) => index).filter((index) => !sharedLeftIndices.has(index) && !sharedRightIndices.has(index));
|
|
373
|
+
return {
|
|
374
|
+
columns: [...sharedPairs.map((pair) => {
|
|
375
|
+
const leftColumn = columnAt(rawColumns, pair.leftIndex, sql);
|
|
376
|
+
const rightColumn = columnAt(rawColumns, leftColumnCount + pair.rightIndex, sql);
|
|
377
|
+
return {
|
|
378
|
+
qualifiers: [.../* @__PURE__ */ new Set([...leftColumn.qualifiers, ...rightColumn.qualifiers])],
|
|
379
|
+
columnName: leftColumn.columnName
|
|
380
|
+
};
|
|
381
|
+
}), ...remainingIndices.map((index) => columnAt(rawColumns, index, sql))],
|
|
382
|
+
rows: rows.map((row) => {
|
|
383
|
+
const mergedValues = sharedPairs.map((pair) => {
|
|
384
|
+
const leftValue = valueAtIndex(row, pair.leftIndex, sql);
|
|
385
|
+
const rightValue = valueAtIndex(row, leftColumnCount + pair.rightIndex, sql);
|
|
386
|
+
return leftValue.kind !== "empty" ? leftValue : rightValue;
|
|
387
|
+
});
|
|
388
|
+
const remainingValues = remainingIndices.map((index) => valueAtIndex(row, index, sql));
|
|
389
|
+
return [...mergedValues, ...remainingValues];
|
|
390
|
+
})
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function applyJoinClause(left, join, tables, sql) {
|
|
394
|
+
const rightTable = resolveTable(tables, join.table, sql);
|
|
395
|
+
const rightColumns = tableColumns(join.alias !== void 0 ? join.alias.name : rightTable.tableName, rightTable);
|
|
396
|
+
const leftColumnCount = left.columns.length;
|
|
397
|
+
const rawColumns = [...left.columns, ...rightColumns];
|
|
398
|
+
let onPredicate;
|
|
399
|
+
let sharedPairs = [];
|
|
400
|
+
if (join.condition.kind === "on") onPredicate = join.condition.predicate;
|
|
401
|
+
else if (join.condition.kind === "using") {
|
|
402
|
+
const allLeftIndices = left.columns.map((_column, index) => index);
|
|
403
|
+
const allRightIndices = rightColumns.map((_column, index) => index);
|
|
404
|
+
sharedPairs = join.condition.columns.map((ref) => {
|
|
405
|
+
const leftIndex = resolveColumnIndex(left.columns, allLeftIndices, ref, sql);
|
|
406
|
+
return {
|
|
407
|
+
leftIndex,
|
|
408
|
+
rightIndex: resolveColumnIndex(rightColumns, allRightIndices, ref, sql),
|
|
409
|
+
columnName: columnAt(left.columns, leftIndex, sql).columnName
|
|
410
|
+
};
|
|
411
|
+
});
|
|
412
|
+
} else if (join.condition.kind === "natural") {
|
|
413
|
+
sharedPairs = computeNaturalSharedPairs(left.columns, rightColumns, sql);
|
|
414
|
+
if (sharedPairs.length === 0) throw new HsqldbSqlEvaluationError("NATURAL JOIN found no columns shared between the joined tables", sql);
|
|
342
415
|
}
|
|
416
|
+
const rawResolver = new ColumnResolver(rawColumns, sql);
|
|
417
|
+
const matchedLeft = /* @__PURE__ */ new Set();
|
|
418
|
+
const matchedRight = /* @__PURE__ */ new Set();
|
|
419
|
+
const paired = [];
|
|
420
|
+
left.rows.forEach((leftRow, leftIndex) => {
|
|
421
|
+
rightTable.rows.forEach((rightRow, rightIndex) => {
|
|
422
|
+
const combined = [...leftRow, ...rightRow];
|
|
423
|
+
if (onPredicate !== void 0 ? evaluatePredicate(onPredicate, combined, rawResolver, sql) === "true" : sharedPairs.length > 0 ? evaluateSharedColumnMatch(combined, sharedPairs, leftColumnCount, sql) : true) {
|
|
424
|
+
paired.push(combined);
|
|
425
|
+
matchedLeft.add(leftIndex);
|
|
426
|
+
matchedRight.add(rightIndex);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
if (join.joinKind === "left" || join.joinKind === "full") left.rows.forEach((leftRow, leftIndex) => {
|
|
431
|
+
if (!matchedLeft.has(leftIndex)) paired.push([...leftRow, ...nullRow(rightColumns.length)]);
|
|
432
|
+
});
|
|
433
|
+
if (join.joinKind === "right" || join.joinKind === "full") rightTable.rows.forEach((rightRow, rightIndex) => {
|
|
434
|
+
if (!matchedRight.has(rightIndex)) paired.push([...nullRow(leftColumnCount), ...rightRow]);
|
|
435
|
+
});
|
|
436
|
+
if (join.condition.kind === "using" || join.condition.kind === "natural") return mergeSharedColumns(rawColumns, paired, sharedPairs, leftColumnCount, sql);
|
|
343
437
|
return {
|
|
344
|
-
|
|
345
|
-
rows
|
|
438
|
+
columns: rawColumns,
|
|
439
|
+
rows: paired
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function joinTables(from, tables, sql) {
|
|
443
|
+
const baseTable = resolveTable(tables, from.table, sql);
|
|
444
|
+
let result = {
|
|
445
|
+
columns: tableColumns(from.alias !== void 0 ? from.alias.name : baseTable.tableName, baseTable),
|
|
446
|
+
rows: baseTable.rows
|
|
346
447
|
};
|
|
448
|
+
for (const join of from.joins) result = applyJoinClause(result, join, tables, sql);
|
|
449
|
+
return result;
|
|
347
450
|
}
|
|
348
451
|
function evaluateSelect(statement, tables) {
|
|
349
|
-
const {
|
|
350
|
-
const resolver = new ColumnResolver(
|
|
452
|
+
const { columns, rows } = joinTables(statement.from, tables, statement.sql);
|
|
453
|
+
const resolver = new ColumnResolver(columns, statement.sql);
|
|
351
454
|
const plan = planSelectList(statement, resolver);
|
|
352
455
|
const where = statement.where;
|
|
353
456
|
const matching = where === void 0 ? rows : rows.filter((row) => evaluatePredicate(where, row, resolver, statement.sql) === "true");
|
package/dist/odb/sql/parser.cjs
CHANGED
|
@@ -13,13 +13,6 @@ function isAggregateFunction(keyword) {
|
|
|
13
13
|
return AGGREGATE_FUNCTIONS.has(keyword);
|
|
14
14
|
}
|
|
15
15
|
const OUT_OF_SCOPE_KEYWORDS = /* @__PURE__ */ new Map([
|
|
16
|
-
["OUTER", "a JOIN"],
|
|
17
|
-
["LEFT", "a JOIN"],
|
|
18
|
-
["RIGHT", "a JOIN"],
|
|
19
|
-
["FULL", "a JOIN"],
|
|
20
|
-
["CROSS", "a JOIN"],
|
|
21
|
-
["NATURAL", "a JOIN"],
|
|
22
|
-
["USING", "a JOIN"],
|
|
23
16
|
["UNION", "UNION"],
|
|
24
17
|
["INTERSECT", "INTERSECT"],
|
|
25
18
|
["EXCEPT", "EXCEPT"],
|
|
@@ -31,7 +24,6 @@ const OUT_OF_SCOPE_KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
31
24
|
["OFFSET", "a row-limit clause (OFFSET)"],
|
|
32
25
|
["FETCH", "a row-limit clause (FETCH)"],
|
|
33
26
|
["TOP", "a row-limit clause (TOP)"],
|
|
34
|
-
["AS", "a column or table alias (AS)"],
|
|
35
27
|
["CASE", "a CASE expression"],
|
|
36
28
|
["WHEN", "a CASE expression"],
|
|
37
29
|
["THEN", "a CASE expression"],
|
|
@@ -175,11 +167,12 @@ var SqlParser = class {
|
|
|
175
167
|
}
|
|
176
168
|
parseSelectItem() {
|
|
177
169
|
const token = this.peek();
|
|
178
|
-
|
|
179
|
-
return {
|
|
170
|
+
const item = token.kind === "keyword" && isAggregateFunction(token.keyword) ? this.parseAggregate(token.keyword) : {
|
|
180
171
|
kind: "column",
|
|
181
172
|
column: this.parseColumnRef()
|
|
182
173
|
};
|
|
174
|
+
if (this.atKeyword("AS")) throw new require_odb_sql_errors.HsqldbSqlUnsupportedError("a column alias (AS)", this.sql);
|
|
175
|
+
return item;
|
|
183
176
|
}
|
|
184
177
|
parseSelectList() {
|
|
185
178
|
if (this.atPunctuation("*")) {
|
|
@@ -439,27 +432,93 @@ var SqlParser = class {
|
|
|
439
432
|
direction: "asc"
|
|
440
433
|
};
|
|
441
434
|
}
|
|
442
|
-
|
|
435
|
+
parseTableRefWithAlias() {
|
|
443
436
|
if (this.atPunctuation("(")) throw new require_odb_sql_errors.HsqldbSqlUnsupportedError("a derived table in FROM", this.sql);
|
|
444
|
-
const
|
|
437
|
+
const table = this.takeName("a table name");
|
|
445
438
|
if (this.atPunctuation(".")) throw new require_odb_sql_errors.HsqldbSqlUnsupportedError("a schema-qualified table name", this.sql);
|
|
446
|
-
|
|
447
|
-
|
|
439
|
+
return {
|
|
440
|
+
table,
|
|
441
|
+
alias: this.parseOptionalTableAlias()
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
parseOptionalTableAlias() {
|
|
445
|
+
if (this.atKeyword("AS")) {
|
|
446
|
+
this.advance();
|
|
447
|
+
return this.takeName("a table alias after AS");
|
|
448
|
+
}
|
|
449
|
+
if (this.peek().kind === "identifier") return this.takeName("a table alias");
|
|
450
|
+
}
|
|
451
|
+
parseJoinKind() {
|
|
452
|
+
if (this.atKeyword("INNER")) {
|
|
453
|
+
this.advance();
|
|
454
|
+
return "inner";
|
|
455
|
+
}
|
|
456
|
+
if (this.atKeyword("LEFT")) {
|
|
457
|
+
this.advance();
|
|
458
|
+
this.consumeOptionalOuter();
|
|
459
|
+
return "left";
|
|
460
|
+
}
|
|
461
|
+
if (this.atKeyword("RIGHT")) {
|
|
462
|
+
this.advance();
|
|
463
|
+
this.consumeOptionalOuter();
|
|
464
|
+
return "right";
|
|
465
|
+
}
|
|
466
|
+
if (this.atKeyword("FULL")) {
|
|
467
|
+
this.advance();
|
|
468
|
+
this.consumeOptionalOuter();
|
|
469
|
+
return "full";
|
|
470
|
+
}
|
|
471
|
+
if (this.atKeyword("CROSS")) {
|
|
472
|
+
this.advance();
|
|
473
|
+
return "cross";
|
|
474
|
+
}
|
|
475
|
+
if (this.atKeyword("JOIN")) return "inner";
|
|
476
|
+
}
|
|
477
|
+
consumeOptionalOuter() {
|
|
478
|
+
if (this.atKeyword("OUTER")) this.advance();
|
|
479
|
+
}
|
|
480
|
+
parseJoinCondition(joinKind, natural) {
|
|
481
|
+
if (natural) return { kind: "natural" };
|
|
482
|
+
if (joinKind === "cross") return { kind: "none" };
|
|
483
|
+
if (this.atKeyword("USING")) {
|
|
484
|
+
this.advance();
|
|
485
|
+
this.takePunctuation("(");
|
|
486
|
+
const columns = [this.takeName("a column name in USING")];
|
|
487
|
+
while (this.atPunctuation(",")) {
|
|
488
|
+
this.advance();
|
|
489
|
+
columns.push(this.takeName("a column name in USING"));
|
|
490
|
+
}
|
|
491
|
+
this.takePunctuation(")");
|
|
492
|
+
return {
|
|
493
|
+
kind: "using",
|
|
494
|
+
columns
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
this.takeKeyword("ON");
|
|
498
|
+
return {
|
|
499
|
+
kind: "on",
|
|
500
|
+
predicate: this.parsePredicate()
|
|
501
|
+
};
|
|
448
502
|
}
|
|
449
503
|
parseJoins() {
|
|
450
504
|
const joins = [];
|
|
451
505
|
for (;;) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
506
|
+
const natural = this.atKeyword("NATURAL");
|
|
507
|
+
if (natural) this.advance();
|
|
508
|
+
const joinKind = this.parseJoinKind();
|
|
509
|
+
if (joinKind === void 0) {
|
|
510
|
+
if (natural) this.fail("keyword JOIN, INNER, LEFT, RIGHT or FULL after NATURAL");
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
if (natural && joinKind === "cross") this.fail("keyword JOIN, INNER, LEFT, RIGHT or FULL after NATURAL (NATURAL CROSS JOIN is not valid SQL)");
|
|
514
|
+
this.takeKeyword("JOIN");
|
|
515
|
+
const { table, alias } = this.parseTableRefWithAlias();
|
|
516
|
+
const condition = this.parseJoinCondition(joinKind, natural);
|
|
460
517
|
joins.push({
|
|
518
|
+
joinKind,
|
|
461
519
|
table,
|
|
462
|
-
|
|
520
|
+
alias,
|
|
521
|
+
condition
|
|
463
522
|
});
|
|
464
523
|
}
|
|
465
524
|
return joins;
|
|
@@ -470,8 +529,10 @@ var SqlParser = class {
|
|
|
470
529
|
this.advance();
|
|
471
530
|
const items = this.parseSelectList();
|
|
472
531
|
this.takeKeyword("FROM");
|
|
532
|
+
const { table, alias } = this.parseTableRefWithAlias();
|
|
473
533
|
const from = {
|
|
474
|
-
table
|
|
534
|
+
table,
|
|
535
|
+
alias,
|
|
475
536
|
joins: this.parseJoins()
|
|
476
537
|
};
|
|
477
538
|
if (this.atPunctuation(",")) throw new require_odb_sql_errors.HsqldbSqlUnsupportedError("a comma-separated FROM list (write an explicit JOIN instead)", this.sql);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SqlJoinClause, c as
|
|
2
|
-
export { SqlAggregateArgument, SqlAggregateFunction, SqlColumnRef, SqlFromClause, SqlJoinClause, SqlLiteral, SqlNameRef, SqlOperand, SqlOrderByTerm, SqlPredicate, SqlSelectItem, SqlSelectStatement, SqlSortDirection, parseSelect };
|
|
1
|
+
import { a as SqlJoinClause, c as SqlLiteral, d as SqlOrderByTerm, f as SqlPredicate, g as parseSelect, h as SqlSortDirection, i as SqlFromClause, l as SqlNameRef, m as SqlSelectStatement, n as SqlAggregateFunction, o as SqlJoinCondition, p as SqlSelectItem, r as SqlColumnRef, s as SqlJoinKind, t as SqlAggregateArgument, u as SqlOperand } from "../../parser-h1n0BflA.cjs";
|
|
2
|
+
export { SqlAggregateArgument, SqlAggregateFunction, SqlColumnRef, SqlFromClause, SqlJoinClause, SqlJoinCondition, SqlJoinKind, SqlLiteral, SqlNameRef, SqlOperand, SqlOrderByTerm, SqlPredicate, SqlSelectItem, SqlSelectStatement, SqlSortDirection, parseSelect };
|
package/dist/odb/sql/parser.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SqlJoinClause, c as
|
|
2
|
-
export { SqlAggregateArgument, SqlAggregateFunction, SqlColumnRef, SqlFromClause, SqlJoinClause, SqlLiteral, SqlNameRef, SqlOperand, SqlOrderByTerm, SqlPredicate, SqlSelectItem, SqlSelectStatement, SqlSortDirection, parseSelect };
|
|
1
|
+
import { a as SqlJoinClause, c as SqlLiteral, d as SqlOrderByTerm, f as SqlPredicate, g as parseSelect, h as SqlSortDirection, i as SqlFromClause, l as SqlNameRef, m as SqlSelectStatement, n as SqlAggregateFunction, o as SqlJoinCondition, p as SqlSelectItem, r as SqlColumnRef, s as SqlJoinKind, t as SqlAggregateArgument, u as SqlOperand } from "../../parser-D-9P_0Qd.js";
|
|
2
|
+
export { SqlAggregateArgument, SqlAggregateFunction, SqlColumnRef, SqlFromClause, SqlJoinClause, SqlJoinCondition, SqlJoinKind, SqlLiteral, SqlNameRef, SqlOperand, SqlOrderByTerm, SqlPredicate, SqlSelectItem, SqlSelectStatement, SqlSortDirection, parseSelect };
|
package/dist/odb/sql/parser.js
CHANGED
|
@@ -12,13 +12,6 @@ function isAggregateFunction(keyword) {
|
|
|
12
12
|
return AGGREGATE_FUNCTIONS.has(keyword);
|
|
13
13
|
}
|
|
14
14
|
const OUT_OF_SCOPE_KEYWORDS = /* @__PURE__ */ new Map([
|
|
15
|
-
["OUTER", "a JOIN"],
|
|
16
|
-
["LEFT", "a JOIN"],
|
|
17
|
-
["RIGHT", "a JOIN"],
|
|
18
|
-
["FULL", "a JOIN"],
|
|
19
|
-
["CROSS", "a JOIN"],
|
|
20
|
-
["NATURAL", "a JOIN"],
|
|
21
|
-
["USING", "a JOIN"],
|
|
22
15
|
["UNION", "UNION"],
|
|
23
16
|
["INTERSECT", "INTERSECT"],
|
|
24
17
|
["EXCEPT", "EXCEPT"],
|
|
@@ -30,7 +23,6 @@ const OUT_OF_SCOPE_KEYWORDS = /* @__PURE__ */ new Map([
|
|
|
30
23
|
["OFFSET", "a row-limit clause (OFFSET)"],
|
|
31
24
|
["FETCH", "a row-limit clause (FETCH)"],
|
|
32
25
|
["TOP", "a row-limit clause (TOP)"],
|
|
33
|
-
["AS", "a column or table alias (AS)"],
|
|
34
26
|
["CASE", "a CASE expression"],
|
|
35
27
|
["WHEN", "a CASE expression"],
|
|
36
28
|
["THEN", "a CASE expression"],
|
|
@@ -174,11 +166,12 @@ var SqlParser = class {
|
|
|
174
166
|
}
|
|
175
167
|
parseSelectItem() {
|
|
176
168
|
const token = this.peek();
|
|
177
|
-
|
|
178
|
-
return {
|
|
169
|
+
const item = token.kind === "keyword" && isAggregateFunction(token.keyword) ? this.parseAggregate(token.keyword) : {
|
|
179
170
|
kind: "column",
|
|
180
171
|
column: this.parseColumnRef()
|
|
181
172
|
};
|
|
173
|
+
if (this.atKeyword("AS")) throw new HsqldbSqlUnsupportedError("a column alias (AS)", this.sql);
|
|
174
|
+
return item;
|
|
182
175
|
}
|
|
183
176
|
parseSelectList() {
|
|
184
177
|
if (this.atPunctuation("*")) {
|
|
@@ -438,27 +431,93 @@ var SqlParser = class {
|
|
|
438
431
|
direction: "asc"
|
|
439
432
|
};
|
|
440
433
|
}
|
|
441
|
-
|
|
434
|
+
parseTableRefWithAlias() {
|
|
442
435
|
if (this.atPunctuation("(")) throw new HsqldbSqlUnsupportedError("a derived table in FROM", this.sql);
|
|
443
|
-
const
|
|
436
|
+
const table = this.takeName("a table name");
|
|
444
437
|
if (this.atPunctuation(".")) throw new HsqldbSqlUnsupportedError("a schema-qualified table name", this.sql);
|
|
445
|
-
|
|
446
|
-
|
|
438
|
+
return {
|
|
439
|
+
table,
|
|
440
|
+
alias: this.parseOptionalTableAlias()
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
parseOptionalTableAlias() {
|
|
444
|
+
if (this.atKeyword("AS")) {
|
|
445
|
+
this.advance();
|
|
446
|
+
return this.takeName("a table alias after AS");
|
|
447
|
+
}
|
|
448
|
+
if (this.peek().kind === "identifier") return this.takeName("a table alias");
|
|
449
|
+
}
|
|
450
|
+
parseJoinKind() {
|
|
451
|
+
if (this.atKeyword("INNER")) {
|
|
452
|
+
this.advance();
|
|
453
|
+
return "inner";
|
|
454
|
+
}
|
|
455
|
+
if (this.atKeyword("LEFT")) {
|
|
456
|
+
this.advance();
|
|
457
|
+
this.consumeOptionalOuter();
|
|
458
|
+
return "left";
|
|
459
|
+
}
|
|
460
|
+
if (this.atKeyword("RIGHT")) {
|
|
461
|
+
this.advance();
|
|
462
|
+
this.consumeOptionalOuter();
|
|
463
|
+
return "right";
|
|
464
|
+
}
|
|
465
|
+
if (this.atKeyword("FULL")) {
|
|
466
|
+
this.advance();
|
|
467
|
+
this.consumeOptionalOuter();
|
|
468
|
+
return "full";
|
|
469
|
+
}
|
|
470
|
+
if (this.atKeyword("CROSS")) {
|
|
471
|
+
this.advance();
|
|
472
|
+
return "cross";
|
|
473
|
+
}
|
|
474
|
+
if (this.atKeyword("JOIN")) return "inner";
|
|
475
|
+
}
|
|
476
|
+
consumeOptionalOuter() {
|
|
477
|
+
if (this.atKeyword("OUTER")) this.advance();
|
|
478
|
+
}
|
|
479
|
+
parseJoinCondition(joinKind, natural) {
|
|
480
|
+
if (natural) return { kind: "natural" };
|
|
481
|
+
if (joinKind === "cross") return { kind: "none" };
|
|
482
|
+
if (this.atKeyword("USING")) {
|
|
483
|
+
this.advance();
|
|
484
|
+
this.takePunctuation("(");
|
|
485
|
+
const columns = [this.takeName("a column name in USING")];
|
|
486
|
+
while (this.atPunctuation(",")) {
|
|
487
|
+
this.advance();
|
|
488
|
+
columns.push(this.takeName("a column name in USING"));
|
|
489
|
+
}
|
|
490
|
+
this.takePunctuation(")");
|
|
491
|
+
return {
|
|
492
|
+
kind: "using",
|
|
493
|
+
columns
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
this.takeKeyword("ON");
|
|
497
|
+
return {
|
|
498
|
+
kind: "on",
|
|
499
|
+
predicate: this.parsePredicate()
|
|
500
|
+
};
|
|
447
501
|
}
|
|
448
502
|
parseJoins() {
|
|
449
503
|
const joins = [];
|
|
450
504
|
for (;;) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
505
|
+
const natural = this.atKeyword("NATURAL");
|
|
506
|
+
if (natural) this.advance();
|
|
507
|
+
const joinKind = this.parseJoinKind();
|
|
508
|
+
if (joinKind === void 0) {
|
|
509
|
+
if (natural) this.fail("keyword JOIN, INNER, LEFT, RIGHT or FULL after NATURAL");
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
if (natural && joinKind === "cross") this.fail("keyword JOIN, INNER, LEFT, RIGHT or FULL after NATURAL (NATURAL CROSS JOIN is not valid SQL)");
|
|
513
|
+
this.takeKeyword("JOIN");
|
|
514
|
+
const { table, alias } = this.parseTableRefWithAlias();
|
|
515
|
+
const condition = this.parseJoinCondition(joinKind, natural);
|
|
459
516
|
joins.push({
|
|
517
|
+
joinKind,
|
|
460
518
|
table,
|
|
461
|
-
|
|
519
|
+
alias,
|
|
520
|
+
condition
|
|
462
521
|
});
|
|
463
522
|
}
|
|
464
523
|
return joins;
|
|
@@ -469,8 +528,10 @@ var SqlParser = class {
|
|
|
469
528
|
this.advance();
|
|
470
529
|
const items = this.parseSelectList();
|
|
471
530
|
this.takeKeyword("FROM");
|
|
531
|
+
const { table, alias } = this.parseTableRefWithAlias();
|
|
472
532
|
const from = {
|
|
473
|
-
table
|
|
533
|
+
table,
|
|
534
|
+
alias,
|
|
474
535
|
joins: this.parseJoins()
|
|
475
536
|
};
|
|
476
537
|
if (this.atPunctuation(",")) throw new HsqldbSqlUnsupportedError("a comma-separated FROM list (write an explicit JOIN instead)", this.sql);
|
|
@@ -88,12 +88,27 @@ interface SqlOrderByTerm {
|
|
|
88
88
|
readonly column: SqlColumnRef;
|
|
89
89
|
readonly direction: SqlSortDirection;
|
|
90
90
|
}
|
|
91
|
+
type SqlJoinKind = "inner" | "left" | "right" | "full" | "cross";
|
|
92
|
+
type SqlJoinCondition = {
|
|
93
|
+
readonly kind: "on";
|
|
94
|
+
readonly predicate: SqlPredicate;
|
|
95
|
+
} | {
|
|
96
|
+
readonly kind: "using";
|
|
97
|
+
readonly columns: readonly SqlNameRef[];
|
|
98
|
+
} | {
|
|
99
|
+
readonly kind: "natural";
|
|
100
|
+
} | {
|
|
101
|
+
readonly kind: "none";
|
|
102
|
+
};
|
|
91
103
|
interface SqlJoinClause {
|
|
104
|
+
readonly joinKind: SqlJoinKind;
|
|
92
105
|
readonly table: SqlNameRef;
|
|
93
|
-
readonly
|
|
106
|
+
readonly alias: SqlNameRef | undefined;
|
|
107
|
+
readonly condition: SqlJoinCondition;
|
|
94
108
|
}
|
|
95
109
|
interface SqlFromClause {
|
|
96
110
|
readonly table: SqlNameRef;
|
|
111
|
+
readonly alias: SqlNameRef | undefined;
|
|
97
112
|
readonly joins: readonly SqlJoinClause[];
|
|
98
113
|
}
|
|
99
114
|
interface SqlSelectStatement {
|
|
@@ -106,4 +121,4 @@ interface SqlSelectStatement {
|
|
|
106
121
|
}
|
|
107
122
|
declare function parseSelect(sql: string): SqlSelectStatement;
|
|
108
123
|
//#endregion
|
|
109
|
-
export { SqlJoinClause as a,
|
|
124
|
+
export { SqlJoinClause as a, SqlLiteral as c, SqlOrderByTerm as d, SqlPredicate as f, parseSelect as g, SqlSortDirection as h, SqlFromClause as i, SqlNameRef as l, SqlSelectStatement as m, SqlAggregateFunction as n, SqlJoinCondition as o, SqlSelectItem as p, SqlColumnRef as r, SqlJoinKind as s, SqlAggregateArgument as t, SqlOperand as u };
|
|
@@ -88,12 +88,27 @@ interface SqlOrderByTerm {
|
|
|
88
88
|
readonly column: SqlColumnRef;
|
|
89
89
|
readonly direction: SqlSortDirection;
|
|
90
90
|
}
|
|
91
|
+
type SqlJoinKind = "inner" | "left" | "right" | "full" | "cross";
|
|
92
|
+
type SqlJoinCondition = {
|
|
93
|
+
readonly kind: "on";
|
|
94
|
+
readonly predicate: SqlPredicate;
|
|
95
|
+
} | {
|
|
96
|
+
readonly kind: "using";
|
|
97
|
+
readonly columns: readonly SqlNameRef[];
|
|
98
|
+
} | {
|
|
99
|
+
readonly kind: "natural";
|
|
100
|
+
} | {
|
|
101
|
+
readonly kind: "none";
|
|
102
|
+
};
|
|
91
103
|
interface SqlJoinClause {
|
|
104
|
+
readonly joinKind: SqlJoinKind;
|
|
92
105
|
readonly table: SqlNameRef;
|
|
93
|
-
readonly
|
|
106
|
+
readonly alias: SqlNameRef | undefined;
|
|
107
|
+
readonly condition: SqlJoinCondition;
|
|
94
108
|
}
|
|
95
109
|
interface SqlFromClause {
|
|
96
110
|
readonly table: SqlNameRef;
|
|
111
|
+
readonly alias: SqlNameRef | undefined;
|
|
97
112
|
readonly joins: readonly SqlJoinClause[];
|
|
98
113
|
}
|
|
99
114
|
interface SqlSelectStatement {
|
|
@@ -106,4 +121,4 @@ interface SqlSelectStatement {
|
|
|
106
121
|
}
|
|
107
122
|
declare function parseSelect(sql: string): SqlSelectStatement;
|
|
108
123
|
//#endregion
|
|
109
|
-
export { SqlJoinClause as a,
|
|
124
|
+
export { SqlJoinClause as a, SqlLiteral as c, SqlOrderByTerm as d, SqlPredicate as f, parseSelect as g, SqlSortDirection as h, SqlFromClause as i, SqlNameRef as l, SqlSelectStatement as m, SqlAggregateFunction as n, SqlJoinCondition as o, SqlSelectItem as p, SqlColumnRef as r, SqlJoinKind as s, SqlAggregateArgument as t, SqlOperand as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js.documents",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.9.0",
|
|
4
4
|
"description": "Bidirectional docx/pptx <-> PDF conversion and a read+write editable OOXML document model, built on ooxml.js and Zod 4 codecs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"rtf-codec": "4.1.3",
|
|
92
92
|
"temml": "0.13.4",
|
|
93
93
|
"wpd-codec": "3.2.1",
|
|
94
|
-
"xls-codec": "4.
|
|
94
|
+
"xls-codec": "4.11.0",
|
|
95
95
|
"zod": "4.4.3"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|