@saltcorn/db-common 1.6.0 → 1.7.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/internal.js CHANGED
@@ -1,25 +1,8 @@
1
- "use strict";
2
1
  /**
3
2
  * @category db-common
4
3
  * @module internal
5
4
  */
6
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
- if (k2 === undefined) k2 = k;
8
- var desc = Object.getOwnPropertyDescriptor(m, k);
9
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
- desc = { enumerable: true, get: function() { return m[k]; } };
11
- }
12
- Object.defineProperty(o, k2, desc);
13
- }) : (function(o, m, k, k2) {
14
- if (k2 === undefined) k2 = k;
15
- o[k2] = m[k];
16
- }));
17
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
- };
20
- Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.dbCommonModulePath = exports.sqlBinOp = exports.sqlFun = exports.prefixFieldsInWhere = exports.mkSelectOptions = exports.orderByIsOperator = exports.orderByIsObject = exports.mkWhere = exports.subSelectWhere = exports.ftsFieldsSqlExpr = exports.sqlsanitizeAllowDots = exports.sqlsanitize = void 0;
22
- __exportStar(require("./dbtypes"), exports);
5
+ export * from "./dbtypes.js";
23
6
  //https://stackoverflow.com/questions/15300704/regex-with-my-jquery-function-for-sql-variable-name-validation
24
7
  /**
25
8
  * Transform value to correct sql name.
@@ -28,9 +11,9 @@ __exportStar(require("./dbtypes"), exports);
28
11
  * @param {string} nm
29
12
  * @returns {string}
30
13
  */
31
- const sqlsanitize = (nm) => {
14
+ export const sqlsanitize = (nm) => {
32
15
  if (typeof nm === "symbol") {
33
- return nm.description ? (0, exports.sqlsanitize)(nm.description) : "";
16
+ return nm.description ? sqlsanitize(nm.description) : "";
34
17
  }
35
18
  // https://stackoverflow.com/a/70273329/19839414
36
19
  // \p{Letter}/u
@@ -40,7 +23,6 @@ const sqlsanitize = (nm) => {
40
23
  else
41
24
  return s;
42
25
  };
43
- exports.sqlsanitize = sqlsanitize;
44
26
  /**
45
27
  * Transform value to correct sql name.
46
28
  * Instead of sqlsanitize also allows .
@@ -50,9 +32,9 @@ exports.sqlsanitize = sqlsanitize;
50
32
  * @param {string} nm
51
33
  * @returns {string}
52
34
  */
53
- const sqlsanitizeAllowDots = (nm) => {
35
+ export const sqlsanitizeAllowDots = (nm) => {
54
36
  if (typeof nm === "symbol") {
55
- return nm.description ? (0, exports.sqlsanitizeAllowDots)(nm.description) : "";
37
+ return nm.description ? sqlsanitizeAllowDots(nm.description) : "";
56
38
  }
57
39
  const s = nm.replace(/[^A-Za-z_0-9."]*/g, "");
58
40
  if (s[0] >= "0" && s[0] <= "9")
@@ -60,48 +42,140 @@ const sqlsanitizeAllowDots = (nm) => {
60
42
  else
61
43
  return s;
62
44
  };
63
- exports.sqlsanitizeAllowDots = sqlsanitizeAllowDots;
45
+ // push each array element and return that many placeholders
46
+ const prepInList = (v, phs) => v.map((current) => phs.push(current)).join(", ");
47
+ /*
48
+ * Dialect used by the boolean-signature mkWhere/mkSelectOptions when
49
+ * is_sqlite is false. Defaults to postgres; a pluggable driver registers its
50
+ * own (see db/index.ts in saltcorn-data) so the many call sites passing
51
+ * db.isSQLite positionally emit SQL for the active driver.
52
+ */
53
+ let defaultDialectFactory = (initCount = 0) => postgresPlaceHolderStack(initCount);
54
+ export const setDefaultDialectFactory = (f) => {
55
+ defaultDialectFactory = f;
56
+ };
64
57
  const postgresPlaceHolderStack = (init = 0) => {
65
58
  let values = [];
66
59
  let i = init;
60
+ const push = (x) => {
61
+ values.push(x);
62
+ i += 1;
63
+ return `$${i}`;
64
+ };
67
65
  return {
68
- push(x) {
69
- values.push(x);
70
- i += 1;
71
- return `$${i}`;
72
- },
66
+ name: "postgres",
73
67
  is_sqlite: false,
68
+ push,
74
69
  getValues() {
75
70
  return values;
76
71
  },
72
+ placeholderAt(n) {
73
+ return `$${n}`;
74
+ },
75
+ like() {
76
+ return "ILIKE";
77
+ },
78
+ regexOperator() {
79
+ return "~";
80
+ },
81
+ castDateExpr(doCast, s) {
82
+ return !doCast ? s : `${s}::date`;
83
+ },
84
+ jsonExtractExpr(fieldExpr, jsonPath, asText) {
85
+ return `${asText ? "jsonb_build_array(" : ""}jsonb_path_query_first(${fieldExpr}, '${jsonPath}')${asText ? ")->>0" : ""}`;
86
+ },
87
+ ftsWhereClause(v) {
88
+ const { fields, table, schema } = v;
89
+ const prefixMatch = !v.searchTerm?.includes(" ");
90
+ const actually_use_websearch = v.use_websearch && !prefixMatch;
91
+ const searchTerm = prefixMatch && !v.disable_fts ? `${v.searchTerm}:*` : v.searchTerm;
92
+ let flds = ftsFieldsSqlExpr(fields, table, schema);
93
+ if (v.disable_fts)
94
+ return `${flds} ILIKE '%' || ${push(v.searchTerm)} || '%'`;
95
+ else if (actually_use_websearch)
96
+ return `to_tsvector('${v.language || "english"}', ${flds}) @@ websearch_to_tsquery('${v.language || "english"}', ${push(searchTerm)})`;
97
+ else if (prefixMatch) {
98
+ // Guard against stop words: to_tsquery('english','on:*') raises a syntax error
99
+ // because stop words are stripped to an empty query. Use websearch_to_tsquery as
100
+ // a probe — it returns ''::tsquery for stop words without erroring. When that
101
+ // happens, fall back to ILIKE so common words like "on", "by", "a" still match.
102
+ const lang = v.language || "english";
103
+ const rawTermPh = push(v.searchTerm);
104
+ const prefixTermPh = push(searchTerm);
105
+ return `CASE WHEN websearch_to_tsquery('${lang}', ${rawTermPh}) = ''::tsquery THEN ${flds} ILIKE '%' || ${rawTermPh} || '%' ELSE to_tsvector('${lang}', ${flds}) @@ to_tsquery('${lang}', ${prefixTermPh}) END`;
106
+ }
107
+ else
108
+ return `to_tsvector('${v.language || "english"}', ${flds}) @@ plainto_tsquery('${v.language || "english"}', ${push(searchTerm)})`;
109
+ },
110
+ arrayInClause(vals) {
111
+ return `= ANY (${push(vals)})`;
112
+ },
113
+ slugifyWhereClause(k, s) {
114
+ return `REGEXP_REPLACE(REPLACE(LOWER(${quote(sqlsanitizeAllowDots(k))}),' ','-'),'[^\\w-]','','g')=${push(s)}`;
115
+ },
116
+ textCastSuffix() {
117
+ return "::text";
118
+ },
77
119
  };
78
120
  };
79
121
  const sqlitePlaceHolderStack = () => {
80
122
  let values = [];
81
- return {
82
- push(x) {
83
- values.push(x);
84
- return `?`;
85
- },
123
+ const push = (x) => {
124
+ values.push(x);
125
+ return `?`;
126
+ };
127
+ const self = {
128
+ name: "sqlite",
86
129
  is_sqlite: true,
130
+ push,
87
131
  getValues() {
88
132
  return values.map((v) => v?.constructor?.name === "PlainDate" ? v.valueOf() : v);
89
133
  },
134
+ placeholderAt() {
135
+ return "?";
136
+ },
137
+ like() {
138
+ return "LIKE";
139
+ },
140
+ regexOperator() {
141
+ return "REGEXP";
142
+ },
143
+ castDateExpr(doCast, s) {
144
+ return !doCast ? s : `date(${s})`;
145
+ },
146
+ jsonExtractExpr(fieldExpr, jsonPath) {
147
+ return `json_extract(${fieldExpr}, '${jsonPath}')`;
148
+ },
149
+ ftsWhereClause(v) {
150
+ const { fields, table, schema } = v;
151
+ let flds = ftsFieldsSqlExpr(fields, table, schema);
152
+ return `${flds} LIKE '%' || ${push(v.searchTerm)} || '%'`;
153
+ },
154
+ arrayInClause(vals) {
155
+ return `IN (${prepInList(vals, self)})`;
156
+ },
157
+ slugifyWhereClause(k, s) {
158
+ return `REPLACE(LOWER(${quote(sqlsanitizeAllowDots(k))}),' ','-')=${push(s)}`;
159
+ },
160
+ textCastSuffix() {
161
+ return "::text";
162
+ },
90
163
  };
164
+ return self;
91
165
  };
92
- const ftsFieldsSqlExpr = (fields, table, schema) => {
166
+ export const ftsFieldsSqlExpr = (fields, table, schema) => {
93
167
  let fldsArray = fields
94
168
  .filter((f) => f.type && f.type.sql_name === "text" && (!f.calculated || f.stored))
95
169
  .map((f) => {
96
170
  const fname = table
97
- ? `"${(0, exports.sqlsanitize)(table)}"."${(0, exports.sqlsanitize)(f.name)}"`
98
- : `"${(0, exports.sqlsanitize)(f.name)}"`;
171
+ ? `"${sqlsanitize(table)}"."${sqlsanitize(f.name)}"`
172
+ : `"${sqlsanitize(f.name)}"`;
99
173
  return `coalesce(${f.type?.searchModifier ? f.type.searchModifier(fname) : fname},'')`;
100
174
  });
101
175
  fields
102
176
  .filter((f) => f.is_fkey && f?.attributes?.include_fts)
103
177
  .forEach((f) => {
104
- fldsArray.push(`coalesce((select "${f.attributes.summary_field}" from ${schema ? `"${schema}".` : ""}"${(0, exports.sqlsanitize)(f.reftable_name)}" rt where rt."${f.refname}"=${table ? `"${(0, exports.sqlsanitize)(table)}".` : ""}"${f.name}"),'')`);
178
+ fldsArray.push(`coalesce((select "${f.attributes.summary_field}" from ${schema ? `"${schema}".` : ""}"${sqlsanitize(f.reftable_name)}" rt where rt."${f.refname}"=${table ? `"${sqlsanitize(table)}".` : ""}"${f.name}"),'')`);
105
179
  });
106
180
  fldsArray.sort();
107
181
  let flds = fldsArray.join(" || ' ' || ");
@@ -109,37 +183,20 @@ const ftsFieldsSqlExpr = (fields, table, schema) => {
109
183
  flds = "''";
110
184
  return flds;
111
185
  };
112
- exports.ftsFieldsSqlExpr = ftsFieldsSqlExpr;
113
- /**
114
- * Where FTS (Search)
115
- * @param {object} v
116
- * @param {string} i
186
+ export /**
187
+ *
117
188
  * @param {boolean} is_sqlite
118
- * @returns {string}
119
- */
120
- const whereFTS = (v, phs) => {
121
- const { fields, table, schema } = v;
122
- const prefixMatch = !v.searchTerm?.includes(" ");
123
- const actually_use_websearch = v.use_websearch && !prefixMatch;
124
- const no_fts = phs.is_sqlite || v.disable_fts;
125
- const searchTerm = prefixMatch && !no_fts ? `${v.searchTerm}:*` : v.searchTerm;
126
- let flds = (0, exports.ftsFieldsSqlExpr)(fields, table, schema);
127
- if (phs.is_sqlite)
128
- return `${flds} LIKE '%' || ${phs.push(v.searchTerm)} || '%'`;
129
- else if (v.disable_fts)
130
- return `${flds} ILIKE '%' || ${phs.push(v.searchTerm)} || '%'`;
131
- else
132
- return `to_tsvector('${v.language || "english"}', ${flds}) @@ ${actually_use_websearch ? "websearch_" : prefixMatch ? "" : `plain`}to_tsquery('${v.language || "english"}', ${phs.push(searchTerm)})`;
133
- };
134
- const subSelectWhere = (phs) => (k, v) => {
189
+ * @param {string} i
190
+ * @returns {function}
191
+ */ const subSelectWhere = (phs) => (k, v) => {
135
192
  const tenantPrefix = !phs.is_sqlite && v.inSelect.tenant ? `"${v.inSelect.tenant}".` : "";
136
193
  if (v.inSelect.through && v.inSelect.valField) {
137
- const whereObj = (0, exports.prefixFieldsInWhere)(v.inSelect.where, "ss2");
194
+ const whereObj = prefixFieldsInWhere(v.inSelect.where, "ss2");
138
195
  const wheres = whereObj ? Object.entries(whereObj) : [];
139
196
  const where = whereObj && wheres.length > 0
140
197
  ? "where " + wheres.map(whereClause(phs)).join(" and ")
141
198
  : "";
142
- return `${quote((0, exports.sqlsanitizeAllowDots)(k))} in (select ss1."${v.inSelect.valField}" from ${tenantPrefix}"${(0, exports.sqlsanitize)(v.inSelect.table)}" ss1 join ${tenantPrefix}"${(0, exports.sqlsanitize)(v.inSelect.through)}" ss2 on ss2."${v.inSelect.through_pk || "id"}" = ss1."${v.inSelect.field}" ${where})`;
199
+ return `${quote(sqlsanitizeAllowDots(k))} in (select ss1."${v.inSelect.valField}" from ${tenantPrefix}"${sqlsanitize(v.inSelect.table)}" ss1 join ${tenantPrefix}"${sqlsanitize(v.inSelect.through)}" ss2 on ss2."${v.inSelect.through_pk || "id"}" = ss1."${v.inSelect.field}" ${where})`;
143
200
  }
144
201
  else {
145
202
  const whereObj = v.inSelect.where;
@@ -147,10 +204,9 @@ const subSelectWhere = (phs) => (k, v) => {
147
204
  const where = whereObj && wheres.length > 0
148
205
  ? "where " + wheres.map(whereClause(phs)).join(" and ")
149
206
  : "";
150
- return `${quote((0, exports.sqlsanitizeAllowDots)(k))} in (select "${v.inSelect.field}" from ${tenantPrefix}"${(0, exports.sqlsanitize)(v.inSelect.table)}" ${where})`;
207
+ return `${quote(sqlsanitizeAllowDots(k))} in (select "${v.inSelect.field}" from ${tenantPrefix}"${sqlsanitize(v.inSelect.table)}" ${where})`;
151
208
  }
152
209
  };
153
- exports.subSelectWhere = subSelectWhere;
154
210
  /**
155
211
  * creates an in select sql string with joins for joinLevels
156
212
  * and the where gets an alias prefix to the first table of the joinLevels
@@ -164,33 +220,33 @@ const inSelectWithLevels = (phs) => (k, v) => {
164
220
  const selectParts = [];
165
221
  const joinLevels = v.inSelectWithLevels.joinLevels;
166
222
  const schema = v.inSelectWithLevels.schema && !phs.is_sqlite
167
- ? `${quote((0, exports.sqlsanitize)(v.inSelectWithLevels.schema))}.`
223
+ ? `${quote(sqlsanitize(v.inSelectWithLevels.schema))}.`
168
224
  : "";
169
225
  for (let i = 0; i < joinLevels.length; i++) {
170
226
  const { table, fkey, inboundKey, pk_name, ref_name } = joinLevels[i];
171
227
  const pk = pk_name || "id";
172
228
  const refname = ref_name || "id";
173
- const alias = quote((0, exports.sqlsanitize)(`${table}SubJ${i}`));
229
+ const alias = quote(sqlsanitize(`${table}SubJ${i}`));
174
230
  if (i === 0) {
175
- selectParts.push(`from ${schema}${quote((0, exports.sqlsanitize)(`${table}`))} ${quote((0, exports.sqlsanitize)(`${alias}`))}`);
176
- whereObj = (0, exports.prefixFieldsInWhere)(v.inSelectWithLevels.where, alias);
231
+ selectParts.push(`from ${schema}${quote(sqlsanitize(`${table}`))} ${quote(sqlsanitize(`${alias}`))}`);
232
+ whereObj = prefixFieldsInWhere(v.inSelectWithLevels.where, alias);
177
233
  if (joinLevels.length === 1)
178
234
  inColumn = quote(`${alias}."${pk}"`);
179
235
  }
180
236
  else if (i < joinLevels.length - 1) {
181
237
  if (fkey) {
182
- selectParts.push(`join ${schema}${quote((0, exports.sqlsanitize)(`${table}`))} ${alias} on ${quote(`${lastAlias}.${(0, exports.sqlsanitize)(fkey)}`)} = ${alias}."${pk}"`);
238
+ selectParts.push(`join ${schema}${quote(sqlsanitize(`${table}`))} ${alias} on ${quote(`${lastAlias}.${sqlsanitize(fkey)}`)} = ${alias}."${pk}"`);
183
239
  }
184
240
  else {
185
- selectParts.push(`join ${schema}${quote((0, exports.sqlsanitize)(`${table}`))} ${alias} on ${quote(`${lastAlias}."${refname}"`)} = ${quote(`${alias}.${(0, exports.sqlsanitize)(inboundKey)}`)}`);
241
+ selectParts.push(`join ${schema}${quote(sqlsanitize(`${table}`))} ${alias} on ${quote(`${lastAlias}."${refname}"`)} = ${quote(`${alias}.${sqlsanitize(inboundKey)}`)}`);
186
242
  }
187
243
  }
188
244
  else {
189
245
  if (fkey) {
190
- inColumn = quote(`${lastAlias}.${(0, exports.sqlsanitize)(fkey)}`);
246
+ inColumn = quote(`${lastAlias}.${sqlsanitize(fkey)}`);
191
247
  }
192
248
  else {
193
- selectParts.push(`join ${schema}${quote((0, exports.sqlsanitize)(`${table}`))} ${alias} on ${quote(`${lastAlias}."${refname}"`)} = ${quote(`${alias}.${(0, exports.sqlsanitize)(`${inboundKey}`)}`)}`);
249
+ selectParts.push(`join ${schema}${quote(sqlsanitize(`${table}`))} ${alias} on ${quote(`${lastAlias}."${refname}"`)} = ${quote(`${alias}.${sqlsanitize(`${inboundKey}`)}`)}`);
194
250
  inColumn = quote(`${alias}."${pk}"`);
195
251
  }
196
252
  }
@@ -200,7 +256,7 @@ const inSelectWithLevels = (phs) => (k, v) => {
200
256
  const where = whereObj && wheres.length > 0
201
257
  ? "where " + wheres.map(whereClause(phs)).join(" and ")
202
258
  : "";
203
- const sqlPart = `${quote((0, exports.sqlsanitizeAllowDots)(k))} in (select ${quote((0, exports.sqlsanitizeAllowDots)(inColumn))} ${selectParts.join(" ")} ${where})`;
259
+ const sqlPart = `${quote(sqlsanitizeAllowDots(k))} in (select ${quote(sqlsanitizeAllowDots(inColumn))} ${selectParts.join(" ")} ${where})`;
204
260
  return sqlPart;
205
261
  };
206
262
  /**
@@ -234,8 +290,8 @@ const whereAnd = (phs) => (ors) => wrapParens(ors
234
290
  .join(" and "));
235
291
  const equals = ([v1, v2], phs) => {
236
292
  const pVal = (v) => typeof v === "symbol"
237
- ? quote((0, exports.sqlsanitizeAllowDots)(v))
238
- : phs.push(v) + (typeof v === "string" ? "::text" : "");
293
+ ? quote(sqlsanitizeAllowDots(v))
294
+ : phs.push(v) + (typeof v === "string" ? phs.textCastSuffix() : "");
239
295
  const isNull = (v) => `${pVal(v)} is null`;
240
296
  if (v1 === null && v2 === null)
241
297
  return "null is null";
@@ -245,43 +301,33 @@ const equals = ([v1, v2], phs) => {
245
301
  return isNull(v1);
246
302
  return `${pVal(v1)}=${pVal(v2)}`;
247
303
  };
248
- const slugifyQuery = (k, s, phs) => phs.is_sqlite
249
- ? `REPLACE(LOWER(${quote((0, exports.sqlsanitizeAllowDots)(k))}),' ','-')=${phs.push(s)}`
250
- : `REGEXP_REPLACE(REPLACE(LOWER(${quote((0, exports.sqlsanitizeAllowDots)(k))}),' ','-'),'[^\\w-]','','g')=${phs.push(s)}`;
251
304
  /**
252
305
  * @param {boolean} is_sqlite
253
306
  * @param {string} i
254
307
  * @returns {function}
255
308
  */
256
- const castDate = (doCast, is_sqlite, s) => !doCast ? s : is_sqlite ? `date(${s})` : `${s}::date`;
257
309
  /*
258
310
  * add elements of the array as single items to the placeholder stack
259
311
  * and return the same amount of placeholders
260
312
  */
261
- const prepSqliteIn = (v, phs) => {
262
- const placeholders = [];
263
- for (const current of v) {
264
- phs.push(current);
265
- placeholders.push("?");
266
- }
267
- return placeholders.join(", ");
268
- };
269
313
  const whereClause = (phs) => ([k, v]) => k === "_fts"
270
- ? whereFTS(v, phs)
314
+ ? phs.ftsWhereClause(v)
271
315
  : typeof (v || {}).not !== "undefined" && v.not.in
272
- ? `not (${quote((0, exports.sqlsanitizeAllowDots)(k))} ${phs.is_sqlite
273
- ? `IN (${prepSqliteIn(v.not.in, phs)})`
274
- : `= ANY (${phs.push(v.not.in)})`})`
316
+ ? // empty list: "not in ()" is always true (and "IN ()" is invalid
317
+ // outside Postgres)
318
+ v.not.in.length === 0
319
+ ? "TRUE"
320
+ : `not (${quote(sqlsanitizeAllowDots(k))} ${phs.arrayInClause(v.not.in)})`
275
321
  : typeof (v || {}).in !== "undefined"
276
- ? `${quote((0, exports.sqlsanitizeAllowDots)(k))} ${phs.is_sqlite
277
- ? `IN (${prepSqliteIn(v.in, phs)})`
278
- : `= ANY (${phs.push(v.in)})`}`
322
+ ? v.in.length === 0
323
+ ? "FALSE"
324
+ : `${quote(sqlsanitizeAllowDots(k))} ${phs.arrayInClause(v.in)}`
279
325
  : k === "or" && Array.isArray(v)
280
326
  ? whereOr(phs)(v)
281
327
  : k === "and" && Array.isArray(v)
282
328
  ? whereAnd(phs)(v)
283
329
  : typeof (v || {}).slugify !== "undefined"
284
- ? slugifyQuery(k, v.slugify, phs)
330
+ ? phs.slugifyWhereClause(k, v.slugify)
285
331
  : k === "not" && typeof v === "object"
286
332
  ? `not (${Object.entries(v)
287
333
  .map((kv) => whereClause(phs)(kv))
@@ -301,22 +347,22 @@ const whereClause = (phs) => ([k, v]) => k === "_fts"
301
347
  .join(" and ")
302
348
  : typeof (v || {}).ilike !== "undefined" &&
303
349
  v.fullMatch
304
- ? `${quote((0, exports.sqlsanitizeAllowDots)(k))} ${phs.is_sqlite ? "LIKE" : "ILIKE"} ${phs.push(v.ilike)}`
350
+ ? `${quote(sqlsanitizeAllowDots(k))} ${phs.like()} ${phs.push(v.ilike)}`
305
351
  : typeof (v || {}).ilike !== "undefined"
306
- ? `${quote((0, exports.sqlsanitizeAllowDots)(k))} ${phs.is_sqlite ? "LIKE" : "ILIKE"} '%' || ${phs.push(v.ilike)} || '%'`
352
+ ? `${quote(sqlsanitizeAllowDots(k))} ${phs.like()} '%' || ${phs.push(v.ilike)} || '%'`
307
353
  : v instanceof RegExp ||
308
354
  v?.constructor?.name === "RegExp"
309
- ? `${quote((0, exports.sqlsanitizeAllowDots)(k))} ${phs.is_sqlite ? "REGEXP" : "~"} ${phs.push(v.source)}`
355
+ ? `${quote(sqlsanitizeAllowDots(k))} ${phs.regexOperator()} ${phs.push(v.source)}`
310
356
  : typeof (v || {}).gt !== "undefined" &&
311
357
  typeof (v || {}).lt !== "undefined"
312
- ? `${castDate(v.day_only, phs.is_sqlite, quote((0, exports.sqlsanitizeAllowDots)(k)))}>${v.equal ? "=" : ""}${castDate(v.day_only, phs.is_sqlite, phs.push(v.gt))} and ${castDate(v.day_only, phs.is_sqlite, quote((0, exports.sqlsanitizeAllowDots)(k)))}<${v.equal ? "=" : ""}${castDate(v.day_only, phs.is_sqlite, phs.push(v.lt))}`
358
+ ? `${phs.castDateExpr(v.day_only, quote(sqlsanitizeAllowDots(k)))}>${v.equal ? "=" : ""}${phs.castDateExpr(v.day_only, phs.push(v.gt))} and ${phs.castDateExpr(v.day_only, quote(sqlsanitizeAllowDots(k)))}<${v.equal ? "=" : ""}${phs.castDateExpr(v.day_only, phs.push(v.lt))}`
313
359
  : typeof (v || {}).gt !== "undefined"
314
- ? `${castDate(v.day_only, phs.is_sqlite, quote((0, exports.sqlsanitizeAllowDots)(k)))}>${v.equal ? "=" : ""}${castDate(v.day_only, phs.is_sqlite, phs.push(v.gt))}`
360
+ ? `${phs.castDateExpr(v.day_only, quote(sqlsanitizeAllowDots(k)))}>${v.equal ? "=" : ""}${phs.castDateExpr(v.day_only, phs.push(v.gt))}`
315
361
  : typeof (v || {}).lt !== "undefined"
316
- ? `${castDate(v.day_only, phs.is_sqlite, quote((0, exports.sqlsanitizeAllowDots)(k)))}<${v.equal ? "=" : ""}${castDate(v.day_only, phs.is_sqlite, phs.push(v.lt))}`
362
+ ? `${phs.castDateExpr(v.day_only, quote(sqlsanitizeAllowDots(k)))}<${v.equal ? "=" : ""}${phs.castDateExpr(v.day_only, phs.push(v.lt))}`
317
363
  : typeof (v || {}).inSelect !==
318
364
  "undefined"
319
- ? (0, exports.subSelectWhere)(phs)(k, v)
365
+ ? subSelectWhere(phs)(k, v)
320
366
  : typeof (v || {})
321
367
  .inSelectWithLevels !==
322
368
  "undefined"
@@ -325,10 +371,10 @@ const whereClause = (phs) => ([k, v]) => k === "_fts"
325
371
  "undefined"
326
372
  ? jsonWhere(k, v.json, phs)
327
373
  : v === null
328
- ? `${quote((0, exports.sqlsanitizeAllowDots)(k))} is null`
374
+ ? `${quote(sqlsanitizeAllowDots(k))} is null`
329
375
  : k === "not"
330
376
  ? `not (${typeof v === "symbol" ? v.description : phs.push(v)})`
331
- : `${quote((0, exports.sqlsanitizeAllowDots)(k))}=${typeof v === "symbol"
377
+ : `${quote(sqlsanitizeAllowDots(k))}=${typeof v === "symbol"
332
378
  ? v.description
333
379
  : phs.push(v)}`;
334
380
  function isdef(x) {
@@ -345,14 +391,12 @@ function jsonWhere(k, v, phs) {
345
391
  : `\$${Array.isArray(sf)
346
392
  ? sf.map(jsonpathElemEscape).join("")
347
393
  : jsonpathElemEscape(sf)}`).replace(/'/g, "''");
348
- const lhs = (f, sf, convText) => phs.is_sqlite
349
- ? `json_extract(${quote((0, exports.sqlsanitizeAllowDots)(f))}, '${jsonpathPrepare(sf)}')`
350
- : `${convText ? "jsonb_build_array(" : ""}jsonb_path_query_first(${quote((0, exports.sqlsanitizeAllowDots)(f))}, '${jsonpathPrepare(sf)}')${convText ? ")->>0" : ""}`;
394
+ const lhs = (f, sf, convText) => phs.jsonExtractExpr(quote(sqlsanitizeAllowDots(f)), jsonpathPrepare(sf), convText);
351
395
  if (Array.isArray(v))
352
396
  return `${lhs(k, v[0], true)}=${phs.push(v[1])}`;
353
397
  else {
354
398
  return andArray(Object.entries(v).map(([kj, vj]) => vj.ilike
355
- ? `${lhs(k, kj, true)} ${phs.is_sqlite ? "LIKE" : "ILIKE"} '%' || ${phs.push(vj.ilike)} || '%'`
399
+ ? `${lhs(k, kj, true)} ${phs.like()} '%' || ${phs.push(vj.ilike)} || '%'`
356
400
  : isdef(vj.gte) || isdef(vj.lte)
357
401
  ? andArray([
358
402
  isdef(vj.gte)
@@ -372,24 +416,26 @@ function andArray(ss) {
372
416
  return ss.join(" and ");
373
417
  }
374
418
  /**
375
- * @param {object} whereObj
376
- * @param {boolean} is_sqlite
377
- * @param {number} initCount
419
+ * Build a where-clause and its bound values for an arbitrary SqlDialect.
420
+ * @param whereObj
421
+ * @param dialect
378
422
  * @returns {object}
379
423
  */
380
- const mkWhere = (whereObj, is_sqlite = false, initCount = 0) => {
424
+ export const mkWhereForDialect = (whereObj, dialect) => {
381
425
  const wheres = whereObj ? Object.entries(whereObj) : [];
382
- //console.log({ wheres });
383
- const placeHolderStack = is_sqlite
384
- ? sqlitePlaceHolderStack()
385
- : postgresPlaceHolderStack(initCount);
386
426
  const where = whereObj && wheres.length > 0
387
- ? "where " + wheres.map(whereClause(placeHolderStack)).join(" and ")
427
+ ? "where " + wheres.map(whereClause(dialect)).join(" and ")
388
428
  : "";
389
- const values = placeHolderStack.getValues();
429
+ const values = dialect.getValues();
390
430
  return { where, values };
391
431
  };
392
- exports.mkWhere = mkWhere;
432
+ /**
433
+ * @param {object} whereObj
434
+ * @param {boolean} is_sqlite
435
+ * @param {number} initCount
436
+ * @returns {object}
437
+ */
438
+ export const mkWhere = (whereObj, is_sqlite = false, initCount = 0) => mkWhereForDialect(whereObj, is_sqlite ? sqlitePlaceHolderStack() : defaultDialectFactory(initCount));
393
439
  /**
394
440
  * @param {number|string} x
395
441
  * @returns {number|null}
@@ -409,9 +455,9 @@ const toInt = (x) => typeof x === "number"
409
455
  */
410
456
  const getDistanceOrder = ({ latField, longField, lat, long }) => {
411
457
  const cos_lat_2 = Math.pow(Math.cos((+lat * Math.PI) / 180), 2);
412
- return `((${(0, exports.sqlsanitizeAllowDots)(`${latField}`)} - ${+lat})*(${(0, exports.sqlsanitizeAllowDots)(`${latField}`)} - ${+lat})) + ((${(0, exports.sqlsanitizeAllowDots)(`${longField}`)} - ${+long})*(${(0, exports.sqlsanitizeAllowDots)(`${longField}`)} - ${+long})*${cos_lat_2})`;
458
+ return `((${sqlsanitizeAllowDots(`${latField}`)} - ${+lat})*(${sqlsanitizeAllowDots(`${latField}`)} - ${+lat})) + ((${sqlsanitizeAllowDots(`${longField}`)} - ${+long})*(${sqlsanitizeAllowDots(`${longField}`)} - ${+long})*${cos_lat_2})`;
413
459
  };
414
- const getOperatorOrder = ({ operator, target, field, }, values, isSQLite) => {
460
+ const getOperatorOrder = ({ operator, target, field, }, values, fmt) => {
415
461
  const validOp = (s) => {
416
462
  if (s.includes("--"))
417
463
  return "";
@@ -436,14 +482,14 @@ const getOperatorOrder = ({ operator, target, field, }, values, isSQLite) => {
436
482
  const ppOp = (ast) => {
437
483
  if (ast === "target") {
438
484
  values.push(target);
439
- return isSQLite ? "?" : `$${values.length}`;
485
+ return fmt.placeholderAt(values.length);
440
486
  }
441
487
  if (ast === "field")
442
- return (0, exports.sqlsanitize)(field);
488
+ return sqlsanitize(field);
443
489
  const { type, name, args } = ast;
444
490
  switch (type) {
445
491
  case "SqlFun":
446
- return `${(0, exports.sqlsanitize)(name)}(${args.map(ppOp).join(",")})`;
492
+ return `${sqlsanitize(name)}(${args.map(ppOp).join(",")})`;
447
493
  case "SqlBinOp":
448
494
  const [arg1, arg2] = args;
449
495
  return `${ppOp(arg1)}${validOp(name)}${ppOp(arg2)}`;
@@ -452,19 +498,18 @@ const getOperatorOrder = ({ operator, target, field, }, values, isSQLite) => {
452
498
  };
453
499
  return ppOp(operator);
454
500
  };
455
- const orderByIsObject = (object) => {
501
+ export const orderByIsObject = (object) => {
456
502
  return object && object.distance;
457
503
  };
458
- exports.orderByIsObject = orderByIsObject;
459
- const orderByIsOperator = (object) => {
504
+ export const orderByIsOperator = (object) => {
460
505
  return object && object.operator && typeof object.operator !== "string";
461
506
  };
462
- exports.orderByIsOperator = orderByIsOperator;
463
507
  /**
464
- * @param {object} selopts
508
+ * Build the "order by / limit / offset / for update" tail of a select for a
509
+ * given dialect. Only fmt.placeholderAt is used (rare operator-orderBy case).
465
510
  * @returns {string}
466
511
  */
467
- const mkSelectOptions = (selopts, values, isSQLite) => {
512
+ export const mkSelectOptionsForDialect = (selopts, values, fmt) => {
468
513
  const orderby = selopts.orderBy === "RANDOM()"
469
514
  ? "order by RANDOM()"
470
515
  : selopts.orderBy &&
@@ -474,22 +519,26 @@ const mkSelectOptions = (selopts, values, isSQLite) => {
474
519
  : selopts.orderBy &&
475
520
  typeof selopts.orderBy === "string" &&
476
521
  selopts.nocase
477
- ? `order by lower(${quote((0, exports.sqlsanitizeAllowDots)(selopts.orderBy))})${selopts.orderDesc ? " DESC" : ""}`
522
+ ? `order by lower(${quote(sqlsanitizeAllowDots(selopts.orderBy))})${selopts.orderDesc ? " DESC" : ""}`
478
523
  : selopts.orderBy && typeof selopts.orderBy === "string"
479
- ? `order by ${quote((0, exports.sqlsanitizeAllowDots)(selopts.orderBy))}${selopts.orderDesc ? " DESC" : ""}`
524
+ ? `order by ${quote(sqlsanitizeAllowDots(selopts.orderBy))}${selopts.orderDesc ? " DESC" : ""}`
480
525
  : selopts.orderBy &&
481
526
  typeof selopts.orderBy === "object" &&
482
527
  "operator" in selopts.orderBy &&
483
528
  typeof selopts.orderBy.operator === "object"
484
- ? `order by ${getOperatorOrder(selopts.orderBy, values, isSQLite)}`
529
+ ? `order by ${getOperatorOrder(selopts.orderBy, values, fmt)}`
485
530
  : "";
486
531
  const limit = selopts.limit ? `limit ${toInt(selopts.limit)}` : "";
487
532
  const offset = selopts.offset ? `offset ${toInt(selopts.offset)}` : "";
488
533
  const forupdate = selopts.forupdate ? "FOR UPDATE" : "";
489
534
  return [orderby, limit, offset, forupdate].filter((s) => s).join(" ");
490
535
  };
491
- exports.mkSelectOptions = mkSelectOptions;
492
- const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
536
+ /**
537
+ * @param {object} selopts
538
+ * @returns {string}
539
+ */
540
+ export const mkSelectOptions = (selopts, values, isSQLite) => mkSelectOptionsForDialect(selopts, values, isSQLite ? sqlitePlaceHolderStack() : defaultDialectFactory());
541
+ export const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
493
542
  if (!inputWhere)
494
543
  return {};
495
544
  const whereObj = {};
@@ -497,17 +546,17 @@ const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
497
546
  if (k === "_fts")
498
547
  whereObj[k] = { table: tablePrefix, ...inputWhere[k] };
499
548
  else if (k === "not") {
500
- whereObj.not = (0, exports.prefixFieldsInWhere)(inputWhere[k], tablePrefix);
549
+ whereObj.not = prefixFieldsInWhere(inputWhere[k], tablePrefix);
501
550
  }
502
551
  else if (k === "or") {
503
552
  whereObj.or = Array.isArray(inputWhere[k])
504
- ? inputWhere[k].map((w) => (0, exports.prefixFieldsInWhere)(w, tablePrefix))
505
- : [(0, exports.prefixFieldsInWhere)(inputWhere[k], tablePrefix)];
553
+ ? inputWhere[k].map((w) => prefixFieldsInWhere(w, tablePrefix))
554
+ : [prefixFieldsInWhere(inputWhere[k], tablePrefix)];
506
555
  }
507
556
  else if (k === "and") {
508
557
  whereObj.and = Array.isArray(inputWhere[k])
509
- ? inputWhere[k].map((w) => (0, exports.prefixFieldsInWhere)(w, tablePrefix))
510
- : (0, exports.prefixFieldsInWhere)(inputWhere[k], tablePrefix);
558
+ ? inputWhere[k].map((w) => prefixFieldsInWhere(w, tablePrefix))
559
+ : prefixFieldsInWhere(inputWhere[k], tablePrefix);
511
560
  }
512
561
  else if (k === "eq") {
513
562
  whereObj[k] = inputWhere[k]; // TODO check for fieldnames
@@ -517,18 +566,15 @@ const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
517
566
  });
518
567
  return whereObj;
519
568
  };
520
- exports.prefixFieldsInWhere = prefixFieldsInWhere;
521
- const sqlFun = (name, ...args) => ({
569
+ export const sqlFun = (name, ...args) => ({
522
570
  type: "SqlFun",
523
571
  name,
524
572
  args,
525
573
  });
526
- exports.sqlFun = sqlFun;
527
- const sqlBinOp = (name, ...args) => ({
574
+ export const sqlBinOp = (name, ...args) => ({
528
575
  type: "SqlBinOp",
529
576
  name,
530
577
  args,
531
578
  });
532
- exports.sqlBinOp = sqlBinOp;
533
- exports.dbCommonModulePath = __dirname;
579
+ export const dbCommonModulePath = import.meta.dirname;
534
580
  //# sourceMappingURL=internal.js.map