@nshiab/simple-data-analysis-core 2.0.5 → 2.0.7

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.
Files changed (244) hide show
  1. package/README.md +118 -12
  2. package/esm/class/Simple.d.ts +8 -2
  3. package/esm/class/Simple.d.ts.map +1 -1
  4. package/esm/class/Simple.js +9 -3
  5. package/esm/class/SimpleDB.d.ts +72 -1
  6. package/esm/class/SimpleDB.d.ts.map +1 -1
  7. package/esm/class/SimpleDB.js +81 -1
  8. package/esm/class/SimpleTable.d.ts +271 -288
  9. package/esm/class/SimpleTable.d.ts.map +1 -1
  10. package/esm/class/SimpleTable.js +269 -340
  11. package/esm/helpers/LogValue.d.ts +8 -0
  12. package/esm/helpers/LogValue.d.ts.map +1 -0
  13. package/esm/helpers/LogValue.js +23 -0
  14. package/esm/helpers/cacheTableDependencies.d.ts +9 -0
  15. package/esm/helpers/cacheTableDependencies.d.ts.map +1 -1
  16. package/esm/helpers/cacheTableDependencies.js +131 -21
  17. package/esm/helpers/cleanSQL.d.ts +1 -1
  18. package/esm/helpers/cleanSQL.d.ts.map +1 -1
  19. package/esm/helpers/cleanSQL.js +268 -23
  20. package/esm/helpers/flushAllTables.d.ts.map +1 -1
  21. package/esm/helpers/flushAllTables.js +23 -28
  22. package/esm/helpers/foldIdentifier.d.ts +3 -0
  23. package/esm/helpers/foldIdentifier.d.ts.map +1 -0
  24. package/esm/helpers/foldIdentifier.js +4 -0
  25. package/esm/helpers/geometryFromJSON.d.ts +3 -0
  26. package/esm/helpers/geometryFromJSON.d.ts.map +1 -0
  27. package/esm/helpers/geometryFromJSON.js +4 -0
  28. package/esm/helpers/index.d.ts +1 -0
  29. package/esm/helpers/index.d.ts.map +1 -1
  30. package/esm/helpers/index.js +1 -0
  31. package/esm/helpers/logData.d.ts.map +1 -1
  32. package/esm/helpers/logData.js +45 -44
  33. package/esm/helpers/makeLogConverter.d.ts +5 -0
  34. package/esm/helpers/makeLogConverter.d.ts.map +1 -0
  35. package/esm/helpers/makeLogConverter.js +64 -0
  36. package/esm/helpers/osmFiles.d.ts.map +1 -1
  37. package/esm/helpers/parseTypes.d.ts +1 -1
  38. package/esm/helpers/parseTypes.d.ts.map +1 -1
  39. package/esm/helpers/parseTypes.js +4 -0
  40. package/esm/helpers/pendingOps.d.ts +4 -2
  41. package/esm/helpers/pendingOps.d.ts.map +1 -1
  42. package/esm/helpers/prepareGeometry.d.ts +3 -0
  43. package/esm/helpers/prepareGeometry.d.ts.map +1 -0
  44. package/esm/helpers/prepareGeometry.js +99 -0
  45. package/esm/helpers/prepareJSON.d.ts +3 -0
  46. package/esm/helpers/prepareJSON.d.ts.map +1 -0
  47. package/esm/helpers/prepareJSON.js +50 -0
  48. package/esm/helpers/printTable.d.ts +5 -4
  49. package/esm/helpers/printTable.d.ts.map +1 -1
  50. package/esm/helpers/printTable.js +32 -36
  51. package/esm/helpers/queryDB.js +1 -1
  52. package/esm/helpers/queueAsyncBarrier.d.ts +4 -0
  53. package/esm/helpers/queueAsyncBarrier.d.ts.map +1 -1
  54. package/esm/helpers/queueAsyncBarrier.js +4 -0
  55. package/esm/helpers/queueOp.d.ts.map +1 -1
  56. package/esm/helpers/queueOp.js +5 -3
  57. package/esm/helpers/readMutationRows.d.ts +9 -0
  58. package/esm/helpers/readMutationRows.d.ts.map +1 -0
  59. package/esm/helpers/readMutationRows.js +94 -0
  60. package/esm/helpers/referencedTables.d.ts +9 -0
  61. package/esm/helpers/referencedTables.d.ts.map +1 -0
  62. package/esm/helpers/referencedTables.js +22 -0
  63. package/esm/helpers/runQuery.d.ts.map +1 -1
  64. package/esm/helpers/runQuery.js +11 -13
  65. package/esm/helpers/scanSQL.d.ts +7 -0
  66. package/esm/helpers/scanSQL.d.ts.map +1 -0
  67. package/esm/helpers/scanSQL.js +71 -0
  68. package/esm/helpers/tableRegistry.d.ts +2 -0
  69. package/esm/helpers/tableRegistry.d.ts.map +1 -1
  70. package/esm/helpers/tableRegistry.js +12 -4
  71. package/esm/helpers/updateColumnsWithJS.d.ts +52 -0
  72. package/esm/helpers/updateColumnsWithJS.d.ts.map +1 -0
  73. package/esm/helpers/updateColumnsWithJS.js +139 -0
  74. package/esm/methods/addColumn.d.ts +1 -1
  75. package/esm/methods/addColumn.d.ts.map +1 -1
  76. package/esm/methods/bins.d.ts +1 -1
  77. package/esm/methods/bins.d.ts.map +1 -1
  78. package/esm/methods/bins.js +45 -29
  79. package/esm/methods/bm25.d.ts.map +1 -1
  80. package/esm/methods/bm25.js +1 -2
  81. package/esm/methods/cache.d.ts.map +1 -1
  82. package/esm/methods/cache.js +4 -6
  83. package/esm/methods/convert.d.ts +2 -2
  84. package/esm/methods/convert.d.ts.map +1 -1
  85. package/esm/methods/convert.js +5 -0
  86. package/esm/methods/getBottom.d.ts +1 -1
  87. package/esm/methods/getBottom.d.ts.map +1 -1
  88. package/esm/methods/getBottom.js +2 -2
  89. package/esm/methods/getData.d.ts +1 -1
  90. package/esm/methods/getData.d.ts.map +1 -1
  91. package/esm/methods/getData.js +2 -2
  92. package/esm/methods/join.d.ts.map +1 -1
  93. package/esm/methods/join.js +8 -16
  94. package/esm/methods/joinGeo.d.ts.map +1 -1
  95. package/esm/methods/joinGeo.js +10 -19
  96. package/esm/methods/loadArray.d.ts +1 -1
  97. package/esm/methods/loadArray.d.ts.map +1 -1
  98. package/esm/methods/loadArray.js +74 -19
  99. package/esm/methods/loadData.js +3 -0
  100. package/esm/methods/log.d.ts +7 -0
  101. package/esm/methods/log.d.ts.map +1 -0
  102. package/esm/methods/log.js +63 -0
  103. package/esm/methods/logBottom.d.ts +5 -0
  104. package/esm/methods/logBottom.d.ts.map +1 -0
  105. package/esm/methods/logBottom.js +9 -0
  106. package/esm/methods/removeTable.d.ts.map +1 -1
  107. package/esm/methods/removeTable.js +2 -0
  108. package/esm/methods/removeTables.d.ts.map +1 -1
  109. package/esm/methods/removeTables.js +7 -0
  110. package/esm/methods/renameTable.d.ts.map +1 -1
  111. package/esm/methods/renameTable.js +4 -0
  112. package/esm/methods/rowToText.js +2 -2
  113. package/esm/methods/selectTables.d.ts.map +1 -1
  114. package/esm/methods/selectTables.js +6 -0
  115. package/esm/methods/setTypes.d.ts +1 -1
  116. package/esm/methods/setTypes.d.ts.map +1 -1
  117. package/esm/methods/stream.d.ts.map +1 -1
  118. package/esm/methods/stream.js +2 -1
  119. package/esm/methods/updateWithJS.d.ts +1 -3
  120. package/esm/methods/updateWithJS.d.ts.map +1 -1
  121. package/esm/methods/updateWithJS.js +123 -37
  122. package/llm.md +365 -461
  123. package/llms.txt +1 -1
  124. package/package.json +1 -1
  125. package/script/class/Simple.d.ts +8 -2
  126. package/script/class/Simple.d.ts.map +1 -1
  127. package/script/class/Simple.js +9 -3
  128. package/script/class/SimpleDB.d.ts +72 -1
  129. package/script/class/SimpleDB.d.ts.map +1 -1
  130. package/script/class/SimpleDB.js +81 -1
  131. package/script/class/SimpleTable.d.ts +271 -288
  132. package/script/class/SimpleTable.d.ts.map +1 -1
  133. package/script/class/SimpleTable.js +269 -340
  134. package/script/helpers/LogValue.d.ts +8 -0
  135. package/script/helpers/LogValue.d.ts.map +1 -0
  136. package/script/helpers/LogValue.js +26 -0
  137. package/script/helpers/cacheTableDependencies.d.ts +9 -0
  138. package/script/helpers/cacheTableDependencies.d.ts.map +1 -1
  139. package/script/helpers/cacheTableDependencies.js +170 -20
  140. package/script/helpers/cleanSQL.d.ts +1 -1
  141. package/script/helpers/cleanSQL.d.ts.map +1 -1
  142. package/script/helpers/cleanSQL.js +271 -23
  143. package/script/helpers/flushAllTables.d.ts.map +1 -1
  144. package/script/helpers/flushAllTables.js +24 -29
  145. package/script/helpers/foldIdentifier.d.ts +3 -0
  146. package/script/helpers/foldIdentifier.d.ts.map +1 -0
  147. package/script/helpers/foldIdentifier.js +7 -0
  148. package/script/helpers/geometryFromJSON.d.ts +3 -0
  149. package/script/helpers/geometryFromJSON.d.ts.map +1 -0
  150. package/script/helpers/geometryFromJSON.js +7 -0
  151. package/script/helpers/index.d.ts +1 -0
  152. package/script/helpers/index.d.ts.map +1 -1
  153. package/script/helpers/index.js +3 -1
  154. package/script/helpers/logData.d.ts.map +1 -1
  155. package/script/helpers/logData.js +45 -44
  156. package/script/helpers/makeLogConverter.d.ts +5 -0
  157. package/script/helpers/makeLogConverter.d.ts.map +1 -0
  158. package/script/helpers/makeLogConverter.js +70 -0
  159. package/script/helpers/osmFiles.d.ts.map +1 -1
  160. package/script/helpers/parseTypes.d.ts +1 -1
  161. package/script/helpers/parseTypes.d.ts.map +1 -1
  162. package/script/helpers/parseTypes.js +4 -0
  163. package/script/helpers/pendingOps.d.ts +4 -2
  164. package/script/helpers/pendingOps.d.ts.map +1 -1
  165. package/script/helpers/prepareGeometry.d.ts +3 -0
  166. package/script/helpers/prepareGeometry.d.ts.map +1 -0
  167. package/script/helpers/prepareGeometry.js +102 -0
  168. package/script/helpers/prepareJSON.d.ts +3 -0
  169. package/script/helpers/prepareJSON.d.ts.map +1 -0
  170. package/script/helpers/prepareJSON.js +53 -0
  171. package/script/helpers/printTable.d.ts +5 -4
  172. package/script/helpers/printTable.d.ts.map +1 -1
  173. package/script/helpers/printTable.js +32 -36
  174. package/script/helpers/queryDB.js +1 -1
  175. package/script/helpers/queueAsyncBarrier.d.ts +4 -0
  176. package/script/helpers/queueAsyncBarrier.d.ts.map +1 -1
  177. package/script/helpers/queueAsyncBarrier.js +4 -0
  178. package/script/helpers/queueOp.d.ts.map +1 -1
  179. package/script/helpers/queueOp.js +4 -2
  180. package/script/helpers/readMutationRows.d.ts +9 -0
  181. package/script/helpers/readMutationRows.d.ts.map +1 -0
  182. package/script/helpers/readMutationRows.js +97 -0
  183. package/script/helpers/referencedTables.d.ts +9 -0
  184. package/script/helpers/referencedTables.d.ts.map +1 -0
  185. package/script/helpers/referencedTables.js +28 -0
  186. package/script/helpers/runQuery.d.ts.map +1 -1
  187. package/script/helpers/runQuery.js +11 -13
  188. package/script/helpers/scanSQL.d.ts +7 -0
  189. package/script/helpers/scanSQL.d.ts.map +1 -0
  190. package/script/helpers/scanSQL.js +74 -0
  191. package/script/helpers/tableRegistry.d.ts +2 -0
  192. package/script/helpers/tableRegistry.d.ts.map +1 -1
  193. package/script/helpers/tableRegistry.js +16 -4
  194. package/script/helpers/updateColumnsWithJS.d.ts +52 -0
  195. package/script/helpers/updateColumnsWithJS.d.ts.map +1 -0
  196. package/script/helpers/updateColumnsWithJS.js +145 -0
  197. package/script/methods/addColumn.d.ts +1 -1
  198. package/script/methods/addColumn.d.ts.map +1 -1
  199. package/script/methods/bins.d.ts +1 -1
  200. package/script/methods/bins.d.ts.map +1 -1
  201. package/script/methods/bins.js +45 -29
  202. package/script/methods/bm25.d.ts.map +1 -1
  203. package/script/methods/bm25.js +1 -2
  204. package/script/methods/cache.d.ts.map +1 -1
  205. package/script/methods/cache.js +3 -5
  206. package/script/methods/convert.d.ts +2 -2
  207. package/script/methods/convert.d.ts.map +1 -1
  208. package/script/methods/convert.js +5 -0
  209. package/script/methods/getBottom.d.ts +1 -1
  210. package/script/methods/getBottom.d.ts.map +1 -1
  211. package/script/methods/getBottom.js +2 -2
  212. package/script/methods/getData.d.ts +1 -1
  213. package/script/methods/getData.d.ts.map +1 -1
  214. package/script/methods/getData.js +2 -2
  215. package/script/methods/join.d.ts.map +1 -1
  216. package/script/methods/join.js +8 -16
  217. package/script/methods/joinGeo.d.ts.map +1 -1
  218. package/script/methods/joinGeo.js +10 -19
  219. package/script/methods/loadArray.d.ts +1 -1
  220. package/script/methods/loadArray.d.ts.map +1 -1
  221. package/script/methods/loadArray.js +74 -19
  222. package/script/methods/loadData.js +3 -0
  223. package/script/methods/log.d.ts +7 -0
  224. package/script/methods/log.d.ts.map +1 -0
  225. package/script/methods/log.js +69 -0
  226. package/script/methods/logBottom.d.ts +5 -0
  227. package/script/methods/logBottom.d.ts.map +1 -0
  228. package/script/methods/logBottom.js +15 -0
  229. package/script/methods/removeTable.d.ts.map +1 -1
  230. package/script/methods/removeTable.js +2 -0
  231. package/script/methods/removeTables.d.ts.map +1 -1
  232. package/script/methods/removeTables.js +7 -0
  233. package/script/methods/renameTable.d.ts.map +1 -1
  234. package/script/methods/renameTable.js +4 -0
  235. package/script/methods/rowToText.js +2 -2
  236. package/script/methods/selectTables.d.ts.map +1 -1
  237. package/script/methods/selectTables.js +6 -0
  238. package/script/methods/setTypes.d.ts +1 -1
  239. package/script/methods/setTypes.d.ts.map +1 -1
  240. package/script/methods/stream.d.ts.map +1 -1
  241. package/script/methods/stream.js +2 -1
  242. package/script/methods/updateWithJS.d.ts +1 -3
  243. package/script/methods/updateWithJS.d.ts.map +1 -1
  244. package/script/methods/updateWithJS.js +122 -36
@@ -0,0 +1,139 @@
1
+ import quoteIdentifier from "./quoteIdentifier.js";
2
+ import readMutationRows from "./readMutationRows.js";
3
+ import { retainRegisteredTables } from "./tableRegistry.js";
4
+ import { executePreparedArray, prepareArray } from "../methods/loadArray.js";
5
+ /**
6
+ * Generates columns from selected input columns, keeping other SQL values in
7
+ * DuckDB. Call from an asynchronous extension barrier. The callback must return
8
+ * one row per input row in the same order; only the declared output columns are
9
+ * stored. Existing output columns are replaced, with types inferred from the
10
+ * generated values. Generation runs sequentially in bounded batches (1,000 rows
11
+ * by default); callbacks must not depend on receiving the whole table. Non-null
12
+ * output types must agree across batches. All-null batches defer type inference.
13
+ * Only one batch of inputs and outputs is retained by this helper in JavaScript;
14
+ * callers must also bound their own buffering. Reads finish before callbacks or
15
+ * writes use the shared connection. The original table is replaced only after
16
+ * staging succeeds. Empty tables are unchanged and do not invoke the callback;
17
+ * outputs that remain null in every batch use VARCHAR.
18
+ *
19
+ * Untouched geometry columns remain in DuckDB, preserving their values, types
20
+ * and CRS without GeoJSON conversion. Geometry columns cannot be selected as
21
+ * inputs or overwritten as outputs; use updateWithJS() for editable GeoJSON.
22
+ * This SQL-only preservation path accepts any source CRS.
23
+ *
24
+ * @param table - Table to enrich.
25
+ * @param inputColumns - Columns available to the callback.
26
+ * @param outputColumns - Generated columns to add or replace.
27
+ * @param generate - Produces one result per input row, in input order.
28
+ * @param options - Controls the maximum rows read, generated, and staged at once.
29
+ * @returns Resolves after merging the generated columns into the table.
30
+ * @example
31
+ * ```ts
32
+ * queueAsyncBarrier(table, {
33
+ * method: "label()",
34
+ * parameters: null,
35
+ * execute: () => updateColumnsWithJS(table, ["name"], ["label"], async (rows) =>
36
+ * rows.map((row) => ({ label: String(row.name).toUpperCase() })),
37
+ * { batchSize: 500 }),
38
+ * });
39
+ * await table.log();
40
+ * ```
41
+ */
42
+ export default async function updateColumnsWithJS(table, inputColumns, outputColumns, generate, options = {}) {
43
+ const batchSize = options.batchSize ?? 1000;
44
+ if (!Number.isSafeInteger(batchSize) || batchSize < 1) {
45
+ throw new Error("batchSize must be a positive safe integer.");
46
+ }
47
+ if (inputColumns.length === 0 || outputColumns.length === 0) {
48
+ throw new Error("Input and output columns must not be empty.");
49
+ }
50
+ if (new Set(outputColumns.map((name) => name.toLowerCase())).size !==
51
+ outputColumns.length) {
52
+ throw new Error("Output columns must be unique.");
53
+ }
54
+ const types = await table.getTypes();
55
+ for (const [name, type] of Object.entries(types)) {
56
+ if (type.toUpperCase().startsWith("GEOMETRY") &&
57
+ [...inputColumns, ...outputColumns].some((column) => column.toLowerCase() === name.toLowerCase())) {
58
+ throw new Error(`Geometry column ${JSON.stringify(name)} cannot be an input or output of updateColumnsWithJS(); use updateWithJS() for editable GeoJSON.`);
59
+ }
60
+ }
61
+ const suffix = crypto.randomUUID().replaceAll("-", "");
62
+ const id = `__sda_id_${suffix}`;
63
+ const snapshot = `__sda_source_${suffix}`;
64
+ const staged = table.sdb.newTable(`__sda_generated_${suffix}`);
65
+ const batch = table.sdb.newTable(`__sda_batch_${suffix}`);
66
+ const q = quoteIdentifier;
67
+ try {
68
+ // A SQL snapshot gives every row a stable identity, including duplicate rows
69
+ // and user columns named rowid. No untouched values cross the JS boundary.
70
+ await table.sdb.customQuery(`CREATE TEMP TABLE ${q(snapshot)} AS SELECT *, row_number() OVER () AS ${q(id)} FROM ${q(table.name)}`);
71
+ const knownTypes = new Map();
72
+ let lastId = "0";
73
+ let hasRows = false;
74
+ while (true) {
75
+ const upperId = BigInt(lastId) + BigInt(batchSize);
76
+ const { rows } = await readMutationRows(table.connection, `SELECT ${q(id)}, ${inputColumns.map(q).join(", ")} FROM ${q(snapshot)} WHERE ${q(id)} > ${lastId} AND ${q(id)} <= ${upperId}
77
+ ORDER BY ${q(id)} LIMIT ${batchSize}`);
78
+ if (rows.length === 0)
79
+ break;
80
+ const ids = rows.map((row) => String(row[id]));
81
+ lastId = ids[ids.length - 1];
82
+ for (const row of rows)
83
+ delete row[id];
84
+ const generated = await generate(rows);
85
+ if (generated.length !== ids.length) {
86
+ throw new Error("Column generation must return one row per input row.");
87
+ }
88
+ // Generated vectors retain this helper's existing inference contract.
89
+ await executePreparedArray(batch, prepareArray(generated.map((row, i) => ({
90
+ ...Object.fromEntries(outputColumns.map((name) => [name, row[name]])),
91
+ [id]: ids[i],
92
+ }))));
93
+ const batchTypes = await batch.getTypes();
94
+ for (const name of outputColumns) {
95
+ const nonNull = generated.some((row) => row[name] !== null && row[name] !== undefined);
96
+ if (!nonNull)
97
+ continue;
98
+ const previous = knownTypes.get(name);
99
+ const current = batchTypes[name];
100
+ if (previous !== undefined && previous !== current) {
101
+ throw new Error(`Generated column ${JSON.stringify(name)} changed type from ${previous} to ${current}.`);
102
+ }
103
+ if (previous === undefined) {
104
+ knownTypes.set(name, current);
105
+ if (hasRows) {
106
+ await table.sdb.customQuery(`ALTER TABLE ${q(staged.name)} ALTER COLUMN ${q(name)} TYPE ${current} USING NULL`);
107
+ }
108
+ }
109
+ }
110
+ if (!hasRows) {
111
+ await table.sdb.customQuery(`CREATE TEMP TABLE ${q(staged.name)} AS SELECT * FROM ${q(batch.name)}`);
112
+ hasRows = true;
113
+ }
114
+ else {
115
+ await table.sdb.customQuery(`INSERT INTO ${q(staged.name)} SELECT * FROM ${q(batch.name)}`);
116
+ }
117
+ }
118
+ if (!hasRows)
119
+ return;
120
+ const outputs = new Map(outputColumns.map((name) => [name.toLowerCase(), name]));
121
+ const columns = Object.keys(types).map((name) => {
122
+ const replacement = outputs.get(name.toLowerCase());
123
+ return replacement === undefined
124
+ ? `original.${q(name)}`
125
+ : `generated.${q(replacement)} AS ${q(name)}`;
126
+ });
127
+ const existing = new Set(Object.keys(types).map((name) => name.toLowerCase()));
128
+ for (const name of outputColumns) {
129
+ if (!existing.has(name.toLowerCase())) {
130
+ columns.push(`generated.${q(name)}`);
131
+ }
132
+ }
133
+ await table.sdb.customQuery(`CREATE OR REPLACE TABLE ${q(table.name)} AS SELECT ${columns.join(", ")} FROM ${q(snapshot)} original JOIN ${q(staged.name)} generated ON original.${q(id)} = CAST(generated.${q(id)} AS BIGINT) ORDER BY original.${q(id)}`);
134
+ }
135
+ finally {
136
+ await table.sdb.customQuery(`DROP TABLE IF EXISTS ${q(snapshot)}; DROP TABLE IF EXISTS ${q(staged.name)}; DROP TABLE IF EXISTS ${q(batch.name)};`);
137
+ retainRegisteredTables(table.sdb, (item) => item !== staged && item !== batch);
138
+ }
139
+ }
@@ -1,3 +1,3 @@
1
1
  import type SimpleTable from "../class/SimpleTable.js";
2
- export default function addColumn(simpleTable: SimpleTable, newColumn: string, type: "integer" | "float" | "number" | "string" | "date" | "time" | "datetime" | "datetimeTz" | "bigint" | "double" | "varchar" | "timestamp" | "timestamp with time zone" | "boolean" | `geometry('${string}')` | `GEOMETRY('${string}')`, definition: string): void;
2
+ export default function addColumn(simpleTable: SimpleTable, newColumn: string, type: "integer" | "float" | "number" | "string" | "date" | "time" | "datetime" | "datetimeTz" | "bigint" | "double" | "varchar" | "timestamp" | "timestamp with time zone" | "boolean" | "json" | "JSON" | `float[${number}]` | `FLOAT[${number}]` | `geometry('${string}')` | `GEOMETRY('${string}')`, definition: string): void;
3
3
  //# sourceMappingURL=addColumn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"addColumn.d.ts","sourceRoot":"","sources":["../../src/methods/addColumn.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,MAAM,EACjB,IAAI,EACA,SAAS,GACT,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,0BAA0B,GAC1B,SAAS,GACT,aAAa,MAAM,IAAI,GACvB,aAAa,MAAM,IAAI,EAC3B,UAAU,EAAE,MAAM,QAuBnB"}
1
+ {"version":3,"file":"addColumn.d.ts","sourceRoot":"","sources":["../../src/methods/addColumn.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,MAAM,EACjB,IAAI,EACA,SAAS,GACT,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,0BAA0B,GAC1B,SAAS,GACT,MAAM,GACN,MAAM,GACN,SAAS,MAAM,GAAG,GAClB,SAAS,MAAM,GAAG,GAClB,aAAa,MAAM,IAAI,GACvB,aAAa,MAAM,IAAI,EAC3B,UAAU,EAAE,MAAM,QAuBnB"}
@@ -1,5 +1,5 @@
1
1
  import type SimpleTable from "../class/SimpleTable.js";
2
- export default function bins(simpleTable: SimpleTable, column: string, interval: number, newColumn: string, options?: {
2
+ export default function bins(simpleTable: SimpleTable, column: string, interval: number, startColumn: string, endColumn: string, options?: {
3
3
  startValue?: number;
4
4
  }): void;
5
5
  //# sourceMappingURL=bins.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bins.d.ts","sourceRoot":"","sources":["../../src/methods/bins.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;CAChB,QAiCP"}
1
+ {"version":3,"file":"bins.d.ts","sourceRoot":"","sources":["../../src/methods/bins.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;CAChB,QAsDP"}
@@ -3,11 +3,20 @@ import assertNewColumns from "../helpers/assertNewColumns.js";
3
3
  import mergeOptions from "../helpers/mergeOptions.js";
4
4
  import queryDB from "../helpers/queryDB.js";
5
5
  import queueOp from "../helpers/queueOp.js";
6
- export default function bins(simpleTable, column, interval, newColumn, options = {}) {
6
+ export default function bins(simpleTable, column, interval, startColumn, endColumn, options = {}) {
7
7
  // This validation does not need the database, so it stays at call time.
8
8
  if (!Number.isFinite(interval) || interval <= 0) {
9
9
  throw new Error("bins() interval must be a finite number greater than 0.");
10
10
  }
11
+ if (typeof startColumn !== "string" || typeof endColumn !== "string") {
12
+ throw new Error("bins() requires start and end output column names.");
13
+ }
14
+ if (startColumn.toLowerCase() === endColumn.toLowerCase()) {
15
+ throw new Error("bins() start and end output column names must be distinct.");
16
+ }
17
+ if (options.startValue !== undefined && !Number.isFinite(options.startValue)) {
18
+ throw new Error("bins() startValue must be a finite number.");
19
+ }
11
20
  // The intervals depend on the minimum and maximum values of the data, so
12
21
  // bins can't be expressed as a single SELECT over its input: it executes
13
22
  // as a barrier.
@@ -15,37 +24,40 @@ export default function bins(simpleTable, column, interval, newColumn, options =
15
24
  queueOp(simpleTable, {
16
25
  kind: "barrier",
17
26
  method: "bins()",
18
- parameters: { column, interval, newColumn, options },
27
+ parameters: { column, interval, startColumn, endColumn, options },
19
28
  execute: async () => {
20
- await queryDB(simpleTable, await binsQuery(simpleTable, column, interval, newColumn, options), mergeOptions(simpleTable, {
29
+ await queryDB(simpleTable, await binsQuery(simpleTable, column, interval, startColumn, endColumn, options), mergeOptions(simpleTable, {
21
30
  table: simpleTable.name,
22
31
  method: "bins()",
23
32
  parameters: {
24
33
  column,
25
34
  interval,
26
- newColumn,
35
+ startColumn,
36
+ endColumn,
27
37
  options,
28
38
  },
29
39
  }));
30
40
  },
31
41
  });
32
42
  }
33
- async function binsQuery(SimpleTable, column, interval, newColumn, options = {}) {
43
+ async function binsQuery(SimpleTable, column, interval, startColumn, endColumn, options = {}) {
34
44
  // A SELECT *, expr AS col colliding with an existing column would be
35
45
  // silently renamed by DuckDB (col -> col_1) instead of erroring, unlike
36
46
  // the ALTER TABLE ADD this used to run.
37
- assertNewColumns(await SimpleTable.getTypes(), [newColumn], "bins()");
47
+ const types = await SimpleTable.getTypes();
48
+ const existingColumns = Object.keys(types);
49
+ assertNewColumns(types, [startColumn, endColumn].map((column) => existingColumns.find((existing) => existing.toLowerCase() === column.toLowerCase()) ?? column), "bins()");
38
50
  // The minimum and maximum are computed in one scan instead of one
39
51
  // getMin/getMax query each.
40
52
  const minMax = await queryDB(SimpleTable, `SELECT MIN(${quoteIdentifier(column)}) AS "min", MAX(${quoteIdentifier(column)}) AS "max" FROM ${quoteIdentifier(SimpleTable.name)}`, mergeOptions(SimpleTable, {
41
53
  table: SimpleTable.name,
42
54
  method: "bins()",
43
- parameters: { column, interval, newColumn, options },
55
+ parameters: { column, interval, startColumn, endColumn, options },
44
56
  returnData: true,
45
57
  }));
46
58
  const minValue = minMax?.[0]?.min;
47
- if (typeof minValue !== "number") {
48
- throw new Error(`minValue of ${column} is not a number`);
59
+ if (typeof minValue !== "number" || !Number.isFinite(minValue)) {
60
+ throw new Error(`minValue of ${column} is not a finite number`);
49
61
  }
50
62
  let startValue;
51
63
  if (typeof options.startValue === "number") {
@@ -58,30 +70,34 @@ async function binsQuery(SimpleTable, column, interval, newColumn, options = {})
58
70
  startValue = minValue;
59
71
  }
60
72
  const maxValue = minMax?.[0]?.max;
61
- if (typeof maxValue !== "number") {
62
- throw new Error(`maxValue of ${column} is not a number`);
73
+ if (typeof maxValue !== "number" || !Number.isFinite(maxValue)) {
74
+ throw new Error(`maxValue of ${column} is not a finite number`);
63
75
  }
64
- const endValue = maxValue;
65
- let increment = 1;
66
- let decimals = 0;
67
- const intervalAsString = interval.toString();
68
- const decimalIndex = intervalAsString.indexOf(".");
69
- if (decimalIndex > 0) {
70
- decimals = intervalAsString.substring(decimalIndex + 1).length;
71
- increment = 1.0 / (10.0 * decimals);
76
+ const starts = [];
77
+ const ends = [];
78
+ // Include the bin beginning at the maximum, even if floating-point division
79
+ // places an exact boundary slightly below its integer index.
80
+ const lastBin = Math.ceil((maxValue - startValue) / interval);
81
+ if (!Number.isSafeInteger(lastBin)) {
82
+ throw new Error("bins() range contains too many intervals.");
72
83
  }
73
- const intervals = [];
74
- for (let i = startValue; i <= endValue; i += interval) {
75
- const start = i;
76
- const end = (i + interval - increment).toFixed(decimals);
77
- intervals.push(`WHEN ${quoteIdentifier(column)} >= ${start} AND ${quoteIdentifier(column)} <= ${end} THEN '[${start}-${end}]'`);
84
+ for (let i = 0; i <= lastBin; i++) {
85
+ // Let SQL evaluate the boundaries from numeric literals, avoiding repeated
86
+ // floating-point addition in JavaScript for fractional intervals.
87
+ // Decimal indices also prevent integer-only expressions from overflowing
88
+ // DuckDB's inferred INT32 type when an end exceeds that range.
89
+ const start = `(${startValue} + ${i}.0 * ${interval})`;
90
+ const end = `(${startValue} + ${i + 1}.0 * ${interval})`;
91
+ const condition = `WHEN ${quoteIdentifier(column)} >= ${start} AND ${quoteIdentifier(column)} < ${end}`;
92
+ starts.push(`${condition} THEN ${start}`);
93
+ ends.push(`${condition} THEN ${end}`);
78
94
  }
79
- // A single rewrite, so the table is scanned once instead of once for the
80
- // ALTER and once for the UPDATE.
95
+ // Keep both outputs numeric, including fractional boundaries. Null source
96
+ // values match no interval and yield null in both output columns.
81
97
  const query = `CREATE OR REPLACE TABLE ${quoteIdentifier(SimpleTable.name)} AS
82
- SELECT *, CASE
83
- ${intervals.join("\n")}
84
- END AS ${quoteIdentifier(newColumn)}
98
+ SELECT *,
99
+ CAST(CASE ${starts.join("\n")} END AS DOUBLE) AS ${quoteIdentifier(startColumn)},
100
+ CAST(CASE ${ends.join("\n")} END AS DOUBLE) AS ${quoteIdentifier(endColumn)}
85
101
  FROM ${quoteIdentifier(SimpleTable.name)}`;
86
102
  return query;
87
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bm25.d.ts","sourceRoot":"","sources":["../../src/methods/bm25.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IACP,OAAO,CAAC,EACJ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,OAAO,GACP,SAAS,GACT,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,OAAO,GACP,WAAW,GACX,YAAY,GACZ,OAAO,GACP,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,YAAY,GACZ,UAAU,GACV,SAAS,GACT,SAAS,GACT,SAAS,GACT,SAAS,GACT,OAAO,GACP,SAAS,GACT,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,WAAW,CA4Bb"}
1
+ {"version":3,"file":"bm25.d.ts","sourceRoot":"","sources":["../../src/methods/bm25.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IACP,OAAO,CAAC,EACJ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,OAAO,GACP,SAAS,GACT,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,OAAO,GACP,WAAW,GACX,YAAY,GACZ,OAAO,GACP,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,YAAY,GACZ,UAAU,GACV,SAAS,GACT,SAAS,GACT,SAAS,GACT,SAAS,GACT,OAAO,GACP,SAAS,GACT,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,WAAW,CA4Bb"}
@@ -1,5 +1,4 @@
1
1
  import quoteIdentifier from "../helpers/quoteIdentifier.js";
2
- import camelCase from "../helpers/camelCase.js";
3
2
  import queryDB from "../helpers/queryDB.js";
4
3
  import mergeOptions from "../helpers/mergeOptions.js";
5
4
  import queueOp from "../helpers/queueOp.js";
@@ -39,7 +38,7 @@ async function executeBm25(simpleTable, outputTable, text, idColumn, textColumn,
39
38
  const selectClause = typeof options.scoreColumn === "string"
40
39
  ? `* EXCLUDE(score), score AS ${quoteIdentifier(options.scoreColumn)}`
41
40
  : `* EXCLUDE(score)`;
42
- await queryDB(simpleTable, `CREATE OR REPLACE TABLE ${quoteIdentifier(outputTable.name)} AS SELECT ${selectClause} FROM (SELECT *, ${quoteIdentifier(`fts_main_${camelCase(simpleTable.name)}`)}.match_bm25(${quoteIdentifier(idColumn)}, ?${typeof options.k === "number" ? `, k := ${options.k}` : ""}${typeof options.b === "number" ? `, b := ${options.b}` : ""}${options.conjunctive === true ? `, conjunctive := 1` : ""}) AS score FROM ${quoteIdentifier(simpleTable.name)}) sq WHERE score NOT NULL${minScoreCondition} ORDER BY score DESC LIMIT ${count};`, mergeOptions(simpleTable, {
41
+ await queryDB(simpleTable, `CREATE OR REPLACE TABLE ${quoteIdentifier(outputTable.name)} AS SELECT ${selectClause} FROM (SELECT *, ${quoteIdentifier(`fts_main_${simpleTable.name}`)}.match_bm25(${quoteIdentifier(idColumn)}, ?${typeof options.k === "number" ? `, k := ${options.k}` : ""}${typeof options.b === "number" ? `, b := ${options.b}` : ""}${options.conjunctive === true ? `, conjunctive := 1` : ""}) AS score FROM ${quoteIdentifier(simpleTable.name)}) sq WHERE score NOT NULL${minScoreCondition} ORDER BY score DESC LIMIT ${count};`, mergeOptions(simpleTable, {
43
42
  table: simpleTable.name,
44
43
  method: "bm25",
45
44
  parameters: {
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/methods/cache.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AA2DvD,wBAA8B,KAAK,CAAC,KAAK,SAAS,WAAW,EAC3D,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC/C,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,iBAwLP"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/methods/cache.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AA4DvD,wBAA8B,KAAK,CAAC,KAAK,SAAS,WAAW,EAC3D,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC/C,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,iBA4LP"}
@@ -6,11 +6,12 @@ import { cacheEntryExpired, cacheLoadMessage, cacheTtlMessage, } from "../helper
6
6
  import serializeCacheInputs, { getCacheInputLabel, } from "../helpers/serializeCacheInputs.js";
7
7
  import { createTableGenerationId, peekTableGeneration, restoreTableGeneration, } from "../helpers/tableGeneration.js";
8
8
  import { loadCacheDatabase, writeCacheDatabase, } from "../helpers/cacheDatabase.js";
9
- import { cacheTableDependenciesMatch, captureCacheTableDependencies, changedCacheTableDependenciesMessage, recordCacheTableAccess, unchangedCacheTableDependenciesMessage, } from "../helpers/cacheTableDependencies.js";
9
+ import { assertCacheTableMutation, cacheTableDependenciesMatch, captureCacheTableDependencies, changedCacheTableDependenciesMessage, recordCacheTableAccess, unchangedCacheTableDependenciesMessage, } from "../helpers/cacheTableDependencies.js";
10
10
  const CACHE_FORMAT_VERSION = "duckdb-v4";
11
11
  export default async function cache(table, compute, options = {}) {
12
12
  // If cache() is itself called from another cache computation, the outer
13
13
  // computation depends on the table produced here.
14
+ assertCacheTableMutation(table);
14
15
  recordCacheTableAccess(table);
15
16
  options.verbose &&
16
17
  console.log(`\ncache() for ${table.name}`);
@@ -31,12 +32,13 @@ export default async function cache(table, compute, options = {}) {
31
32
  const inputs = options.inputs?.filter((input) => input !== table) ?? [];
32
33
  const hasInputs = inputs.length > 0;
33
34
  const serializedInputs = hasInputs ? serializeCacheInputs(inputs) : null;
34
- const codeHash = createHash(functionBody);
35
+ const codeHash = createHash(JSON.stringify([table.sdb.expressionSyntax, functionBody]));
35
36
  const inputHashes = hasInputs
36
37
  ? inputs.map((input) => createHash(serializeCacheInputs([input])))
37
38
  : [];
38
39
  const hash = createHash(JSON.stringify([
39
40
  CACHE_FORMAT_VERSION,
41
+ table.sdb.expressionSyntax,
40
42
  table.name,
41
43
  entryGeneration,
42
44
  functionBody,
@@ -119,10 +121,6 @@ async function runAndWrite(table, compute, cacheSources, cacheSourcesPath, cache
119
121
  const start = Date.now();
120
122
  const tableDependencies = await captureCacheTableDependencies(table, async () => {
121
123
  await compute(table);
122
- // run() only queues the sync builders in the user's callback; the actual
123
- // computation happens at the flush, so it must be included in the timing
124
- // that decides how much the cache saves on later hits.
125
- await flushAllTables(table.sdb);
126
124
  });
127
125
  const end = Date.now();
128
126
  const duration = end - start;
@@ -1,12 +1,12 @@
1
1
  import type SimpleTable from "../class/SimpleTable.js";
2
2
  import type { TableSchema } from "../helpers/pendingOps.js";
3
3
  export default function convert(simpleTable: SimpleTable, types: {
4
- [key: string]: "integer" | "float" | "number" | "string" | "date" | "time" | "datetime" | "datetimeTz" | "bigint" | "double" | "varchar" | "timestamp" | "timestamp with time zone" | "boolean";
4
+ [key: string]: "integer" | "float" | "number" | "string" | "date" | "time" | "datetime" | "datetimeTz" | "bigint" | "double" | "varchar" | "timestamp" | "timestamp with time zone" | "boolean" | "json" | "JSON" | `float[${number}]` | `FLOAT[${number}]` | `geometry('${string}')` | `GEOMETRY('${string}')`;
5
5
  }, options?: {
6
6
  strict?: boolean;
7
7
  datetimeFormat?: string;
8
8
  }): void;
9
- export declare function convertSelect(input: string, columns: string[], columnsTypes: ("integer" | "float" | "number" | "string" | "date" | "time" | "datetime" | "datetimeTz" | "bigint" | "double" | "varchar" | "timestamp" | "timestamp with time zone" | "boolean")[], allColumns: string[], allTypes: TableSchema, options: {
9
+ export declare function convertSelect(input: string, columns: string[], columnsTypes: ("integer" | "float" | "number" | "string" | "date" | "time" | "datetime" | "datetimeTz" | "bigint" | "double" | "varchar" | "timestamp" | "timestamp with time zone" | "boolean" | "json" | "JSON" | `float[${number}]` | `FLOAT[${number}]` | `geometry('${string}')` | `GEOMETRY('${string}')`)[], allColumns: string[], allTypes: TableSchema, options: {
10
10
  datetimeFormat?: string;
11
11
  strict?: boolean;
12
12
  }): string;
@@ -1 +1 @@
1
- {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/methods/convert.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE;IACL,CAAC,GAAG,EAAE,MAAM,GACR,SAAS,GACT,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,0BAA0B,GAC1B,SAAS,CAAC;CACf,EACD,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACpB,QA+CP;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,YAAY,EAAE,CACV,SAAS,GACT,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,0BAA0B,GAC1B,SAAS,CACZ,EAAE,EACH,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,UAgFvD"}
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/methods/convert.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE;IACL,CAAC,GAAG,EAAE,MAAM,GACR,SAAS,GACT,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,0BAA0B,GAC1B,SAAS,GACT,MAAM,GACN,MAAM,GACN,SAAS,MAAM,GAAG,GAClB,SAAS,MAAM,GAAG,GAClB,aAAa,MAAM,IAAI,GACvB,aAAa,MAAM,IAAI,CAAC;CAC7B,EACD,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACpB,QAkDP;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,YAAY,EAAE,CACV,SAAS,GACT,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,0BAA0B,GAC1B,SAAS,GACT,MAAM,GACN,MAAM,GACN,SAAS,MAAM,GAAG,GAClB,SAAS,MAAM,GAAG,GAClB,aAAa,MAAM,IAAI,GACvB,aAAa,MAAM,IAAI,CAC1B,EAAE,EACH,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,UAuFvD"}
@@ -9,6 +9,7 @@ export default function convert(simpleTable, types, options = {}) {
9
9
  method: "convert()",
10
10
  parameters: { types, options },
11
11
  needsSchema: true,
12
+ needsSpatial: Object.values(types).some((type) => type.toLowerCase().startsWith("geometry")),
12
13
  outputSchema: (allTypes) => ({
13
14
  ...allTypes,
14
15
  ...Object.fromEntries(Object.entries(types).map(([column, type]) => [
@@ -78,6 +79,10 @@ export function convertSelect(input, columns, columnsTypes, allColumns, allTypes
78
79
  else if (msToTimestamp) {
79
80
  query += ` TIMESTAMP '1970-01-01 00:00:00' + to_milliseconds(${quoteIdentifier(column)}) AS ${quoteIdentifier(column)},`;
80
81
  }
82
+ else if (options.strict === false && expectedType.startsWith("GEOMETRY")) {
83
+ // DuckDB's geometry parser can throw even inside TRY_CAST.
84
+ query += ` TRY(CAST(${quoteIdentifier(column)} AS ${expectedType})) AS ${quoteIdentifier(column)},`;
85
+ }
81
86
  else if (stringToNumber) {
82
87
  // Thousand separators would make the cast fail.
83
88
  query += ` ${cast}(REPLACE(${quoteIdentifier(column)}, ',', '') AS ${parseType(columnsTypes[indexOf])}) AS ${quoteIdentifier(column)},`;
@@ -2,7 +2,7 @@ import type SimpleTable from "../class/SimpleTable.js";
2
2
  export default function getBottom(simpleTable: SimpleTable, count: number, options?: {
3
3
  originalOrder?: boolean;
4
4
  conditions?: string;
5
- }): Promise<{
5
+ }, method?: "getBottom()" | "logBottom()"): Promise<{
6
6
  [key: string]: unknown;
7
7
  }[]>;
8
8
  //# sourceMappingURL=getBottom.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getBottom.d.ts","sourceRoot":"","sources":["../../src/methods/getBottom.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,wBAA8B,SAAS,CACrC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CAChB;;KA6BP"}
1
+ {"version":3,"file":"getBottom.d.ts","sourceRoot":"","sources":["../../src/methods/getBottom.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,wBAA8B,SAAS,CACrC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CAChB,EACN,MAAM,GAAE,aAAa,GAAG,aAA6B;;KA6BtD"}
@@ -1,14 +1,14 @@
1
1
  import quoteIdentifier from "../helpers/quoteIdentifier.js";
2
2
  import mergeOptions from "../helpers/mergeOptions.js";
3
3
  import queryDB from "../helpers/queryDB.js";
4
- export default async function getBottom(simpleTable, count, options = {}) {
4
+ export default async function getBottom(simpleTable, count, options = {}, method = "getBottom()") {
5
5
  const queryResult = await queryDB(simpleTable, `WITH "numberedRowsForGetBottom" AS (
6
6
  SELECT *, row_number() OVER () as "rowNumberForGetBottom" FROM ${quoteIdentifier(simpleTable.name)}${options.conditions ? ` WHERE ${options.conditions}` : ""}
7
7
  )
8
8
  SELECT * FROM "numberedRowsForGetBottom" ORDER BY "rowNumberForGetBottom" DESC LIMIT ${count};`, mergeOptions(simpleTable, {
9
9
  table: simpleTable.name,
10
10
  returnData: true,
11
- method: "getBottom()",
11
+ method,
12
12
  parameters: { count, options },
13
13
  }));
14
14
  if (!queryResult) {
@@ -3,7 +3,7 @@ export default function getData(simpleTable: SimpleTable, options?: {
3
3
  columns?: string | string[];
4
4
  conditions?: string;
5
5
  limit?: number;
6
- }): Promise<{
6
+ }, method?: "getData()" | "log()"): Promise<{
7
7
  [key: string]: unknown;
8
8
  }[]>;
9
9
  //# sourceMappingURL=getData.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getData.d.ts","sourceRoot":"","sources":["../../src/methods/getData.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAIvD,wBAA8B,OAAO,CACnC,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX;;KAmCP"}
1
+ {"version":3,"file":"getData.d.ts","sourceRoot":"","sources":["../../src/methods/getData.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAIvD,wBAA8B,OAAO,CACnC,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,EACN,MAAM,GAAE,WAAW,GAAG,OAAqB;;KAmC5C"}
@@ -2,7 +2,7 @@ import mergeOptions from "../helpers/mergeOptions.js";
2
2
  import queryDB from "../helpers/queryDB.js";
3
3
  import quoteIdentifier from "../helpers/quoteIdentifier.js";
4
4
  import quoteQualifiedIdentifier from "../helpers/quoteQualifiedIdentifier.js";
5
- export default async function getData(simpleTable, options = {}) {
5
+ export default async function getData(simpleTable, options = {}, method = "getData()") {
6
6
  if (options.limit !== undefined &&
7
7
  (!Number.isInteger(options.limit) || options.limit < 0)) {
8
8
  throw new Error("getData() limit must be an integer greater than or equal to 0.");
@@ -17,7 +17,7 @@ export default async function getData(simpleTable, options = {}) {
17
17
  : "*"} from ${quoteIdentifier(simpleTable.name)}${options.conditions ? ` WHERE ${options.conditions}` : ""}${options.limit === undefined ? "" : ` LIMIT ${options.limit}`}`, mergeOptions(simpleTable, {
18
18
  returnData: true,
19
19
  table: simpleTable.name,
20
- method: "getData()",
20
+ method,
21
21
  parameters: { options },
22
22
  })));
23
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"join.d.ts","sourceRoot":"","sources":["../../src/methods/join.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAOvD,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,SAAS,EAAE,WAAW,EACtB,UAAU,EAAE,WAAW,EACvB,OAAO,GAAE;IACP,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B,GACL,WAAW,CAiBb"}
1
+ {"version":3,"file":"join.d.ts","sourceRoot":"","sources":["../../src/methods/join.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAKvD,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,SAAS,EAAE,WAAW,EACtB,UAAU,EAAE,WAAW,EACvB,OAAO,GAAE;IACP,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B,GACL,WAAW,CAkBb"}
@@ -1,7 +1,5 @@
1
1
  import quoteIdentifier from "../helpers/quoteIdentifier.js";
2
2
  import getIdenticalColumns from "../helpers/getIdenticalColumns.js";
3
- import mergeOptions from "../helpers/mergeOptions.js";
4
- import queryDB from "../helpers/queryDB.js";
5
3
  import queueOp from "../helpers/queueOp.js";
6
4
  import assertSameDatabase from "../helpers/assertSameDatabase.js";
7
5
  export default function join(leftTable, rightTable, options = {}) {
@@ -13,14 +11,15 @@ export default function join(leftTable, rightTable, options = {}) {
13
11
  ? leftTable.sdb.newTable(options.outputTable)
14
12
  : leftTable;
15
13
  queueOp(outputTable, {
16
- kind: "barrier",
14
+ kind: "source",
15
+ rawSQL: [quoteIdentifier(leftTable.name), quoteIdentifier(rightTable.name)],
17
16
  method: "join()",
18
17
  parameters: { rightTable: rightTable.name, options },
19
- execute: () => executeJoin(leftTable, rightTable, outputTable, options),
18
+ buildSelect: () => buildJoinSelect(leftTable, rightTable, options),
20
19
  });
21
20
  return outputTable;
22
21
  }
23
- async function executeJoin(leftTable, rightTable, outputTable, options) {
22
+ async function buildJoinSelect(leftTable, rightTable, options) {
24
23
  const leftTableColumns = await leftTable.getColumns();
25
24
  const rightTableColumns = await rightTable.getColumns();
26
25
  const identicalColumns = getIdenticalColumns(leftTableColumns, rightTableColumns);
@@ -74,17 +73,10 @@ async function executeJoin(leftTable, rightTable, outputTable, options) {
74
73
  .filter((d) => !on.includes(d))
75
74
  .map((d) => `${quoteIdentifier(rightTable.name)}.${quoteIdentifier(d)}`),
76
75
  ].join(", ");
77
- await queryDB(leftTable, joinQuery(leftTable.name, rightTable.name, on, type, outputTable.name, selectList), mergeOptions(leftTable, {
78
- table: outputTable.name,
79
- method: "join()",
80
- parameters: {
81
- rightTable: rightTable.name,
82
- options,
83
- },
84
- }));
76
+ return joinQuery(leftTable.name, rightTable.name, on, type, selectList);
85
77
  }
86
- function joinQuery(leftTable, rightTable, on, join, outputTable, selectList) {
87
- let query = `CREATE OR REPLACE TABLE ${quoteIdentifier(outputTable)} AS SELECT ${selectList}`;
78
+ function joinQuery(leftTable, rightTable, on, join, selectList) {
79
+ let query = `SELECT ${selectList}`;
88
80
  if (join === "inner") {
89
81
  query += ` FROM ${quoteIdentifier(leftTable)} JOIN ${quoteIdentifier(rightTable)}`;
90
82
  }
@@ -101,6 +93,6 @@ function joinQuery(leftTable, rightTable, on, join, outputTable, selectList) {
101
93
  throw new Error(`Unknown ${join} join.`);
102
94
  }
103
95
  query += ` ON (${on.map((d) => `${quoteIdentifier(leftTable)}.${quoteIdentifier(d)} = ${quoteIdentifier(rightTable)}.${quoteIdentifier(d)}`)
104
- .join(" AND ")});\n`;
96
+ .join(" AND ")})`;
105
97
  return query;
106
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"joinGeo.d.ts","sourceRoot":"","sources":["../../src/methods/joinGeo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAQvD,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,gBAAgB,EACjD,UAAU,EAAE,WAAW,EACvB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC;IAClD,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B,GACL,WAAW,CAmBb"}
1
+ {"version":3,"file":"joinGeo.d.ts","sourceRoot":"","sources":["../../src/methods/joinGeo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,gBAAgB,EACjD,UAAU,EAAE,WAAW,EACvB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC;IAClD,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B,GACL,WAAW,CAqBb"}
@@ -2,8 +2,6 @@ import quoteIdentifier from "../helpers/quoteIdentifier.js";
2
2
  import capitalize from "../helpers/capitalize.js";
3
3
  import findGeoColumnFromSchema from "../helpers/findGeoColumnFromSchema.js";
4
4
  import getIdenticalColumns from "../helpers/getIdenticalColumns.js";
5
- import mergeOptions from "../helpers/mergeOptions.js";
6
- import queryDB from "../helpers/queryDB.js";
7
5
  import queueOp from "../helpers/queueOp.js";
8
6
  import assertSameDatabase from "../helpers/assertSameDatabase.js";
9
7
  export default function joinGeo(leftTable, method, rightTable, options = {}) {
@@ -16,10 +14,12 @@ export default function joinGeo(leftTable, method, rightTable, options = {}) {
16
14
  ? leftTable.sdb.newTable(options.outputTable)
17
15
  : leftTable;
18
16
  queueOp(outputTable, {
19
- kind: "barrier",
17
+ kind: "source",
18
+ rawSQL: [quoteIdentifier(leftTable.name), quoteIdentifier(rightTable.name)],
19
+ needsSpatial: true,
20
20
  method: "joinGeo()",
21
21
  parameters: { method, rightTable: rightTable.name, options },
22
- execute: () => executeJoinGeo(leftTable, method, rightTable, outputTable, options),
22
+ buildSelect: () => buildJoinGeoSelect(leftTable, method, rightTable, options),
23
23
  });
24
24
  return outputTable;
25
25
  }
@@ -43,7 +43,7 @@ function validateJoinGeoOptions(method, options) {
43
43
  function formatReceivedValue(value) {
44
44
  return JSON.stringify(value) ?? String(value);
45
45
  }
46
- async function executeJoinGeo(leftTable, method, rightTable, outputTable, options) {
46
+ async function buildJoinGeoSelect(leftTable, method, rightTable, options) {
47
47
  const leftTypes = await leftTable.getTypes();
48
48
  const rightTypes = await rightTable.getTypes();
49
49
  const leftColumn = options.leftColumn ??
@@ -83,19 +83,10 @@ async function executeJoinGeo(leftTable, method, rightTable, outputTable, option
83
83
  ]),
84
84
  ].join(", ");
85
85
  const type = options.type ?? "left";
86
- await queryDB(leftTable, joinGeoQuery(leftTable.name, leftColumn, method, rightTable.name, rightColumn, type, outputTable.name, selectList, options.distance, options.distanceMethod), mergeOptions(leftTable, {
87
- table: outputTable.name,
88
- method: "joinGeo()",
89
- parameters: {
90
- leftTable: leftTable.name,
91
- method,
92
- rightTable: rightTable.name,
93
- options,
94
- },
95
- }));
86
+ return joinGeoQuery(leftTable.name, leftColumn, method, rightTable.name, rightColumn, type, selectList, options.distance, options.distanceMethod);
96
87
  }
97
- function joinGeoQuery(leftTable, leftColumn, method, rightTable, rightColumn, join, outputTable, selectList, distance, distanceMethod) {
98
- let query = `CREATE OR REPLACE TABLE ${quoteIdentifier(outputTable)} AS SELECT ${selectList}`;
88
+ function joinGeoQuery(leftTable, leftColumn, method, rightTable, rightColumn, join, selectList, distance, distanceMethod) {
89
+ let query = `SELECT ${selectList}`;
99
90
  if (join === "inner") {
100
91
  query += ` FROM ${quoteIdentifier(leftTable)} JOIN ${quoteIdentifier(rightTable)}`;
101
92
  }
@@ -112,11 +103,11 @@ function joinGeoQuery(leftTable, leftColumn, method, rightTable, rightColumn, jo
112
103
  throw new Error(`Unknown ${join} join.`);
113
104
  }
114
105
  if (method === "intersect") {
115
- query += ` ON ST_Intersects(${quoteIdentifier(leftTable)}.${quoteIdentifier(leftColumn)}, ${quoteIdentifier(rightTable)}.${quoteIdentifier(rightColumn)});`;
106
+ query += ` ON ST_Intersects(${quoteIdentifier(leftTable)}.${quoteIdentifier(leftColumn)}, ${quoteIdentifier(rightTable)}.${quoteIdentifier(rightColumn)})`;
116
107
  }
117
108
  else if (method === "inside") {
118
109
  // Order is important
119
- query += ` ON ST_Covers(${quoteIdentifier(rightTable)}.${quoteIdentifier(rightColumn)}, ${quoteIdentifier(leftTable)}.${quoteIdentifier(leftColumn)});`;
110
+ query += ` ON ST_Covers(${quoteIdentifier(rightTable)}.${quoteIdentifier(rightColumn)}, ${quoteIdentifier(leftTable)}.${quoteIdentifier(leftColumn)})`;
120
111
  }
121
112
  else if (method === "withinDistance") {
122
113
  if (typeof distance === "number") {