@saltcorn/db-common 1.6.3-beta.1 → 1.7.0-alpha.1

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,25 +416,27 @@ 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
- const whereClauses = wheres.map(whereClause(placeHolderStack)).join(" and ");
426
+ const whereClauses = wheres.map(whereClause(dialect)).join(" and ");
387
427
  const where = whereObj && wheres.length > 0 && whereClauses.length
388
428
  ? "where " + whereClauses
389
429
  : "";
390
- const values = placeHolderStack.getValues();
430
+ const values = dialect.getValues();
391
431
  return { where, values };
392
432
  };
393
- exports.mkWhere = mkWhere;
433
+ /**
434
+ * @param {object} whereObj
435
+ * @param {boolean} is_sqlite
436
+ * @param {number} initCount
437
+ * @returns {object}
438
+ */
439
+ export const mkWhere = (whereObj, is_sqlite = false, initCount = 0) => mkWhereForDialect(whereObj, is_sqlite ? sqlitePlaceHolderStack() : defaultDialectFactory(initCount));
394
440
  /**
395
441
  * @param {number|string} x
396
442
  * @returns {number|null}
@@ -410,9 +456,11 @@ const toInt = (x) => typeof x === "number"
410
456
  */
411
457
  const getDistanceOrder = ({ latField, longField, lat, long }) => {
412
458
  const cos_lat_2 = Math.pow(Math.cos((+lat * Math.PI) / 180), 2);
413
- return `((${(0, exports.sqlsanitizeAllowDots)(`${latField}`)} - ${+lat})*(${(0, exports.sqlsanitizeAllowDots)(`${latField}`)} - ${+lat})) + ((${(0, exports.sqlsanitizeAllowDots)(`${longField}`)} - ${+long})*(${(0, exports.sqlsanitizeAllowDots)(`${longField}`)} - ${+long})*${cos_lat_2})`;
459
+ const latf = `"${sqlsanitizeAllowDots(`${latField}`)}"`;
460
+ const longf = `"${sqlsanitizeAllowDots(`${longField}`)}"`;
461
+ return `((${latf} - ${+lat})*(${latf} - ${+lat})) + ((${longf} - ${+long})*(${longf} - ${+long})*${cos_lat_2})`;
414
462
  };
415
- const getOperatorOrder = ({ operator, target, field, }, values, isSQLite) => {
463
+ const getOperatorOrder = ({ operator, target, field, }, values, fmt) => {
416
464
  const validOp = (s) => {
417
465
  if (s.includes("--"))
418
466
  return "";
@@ -437,14 +485,14 @@ const getOperatorOrder = ({ operator, target, field, }, values, isSQLite) => {
437
485
  const ppOp = (ast) => {
438
486
  if (ast === "target") {
439
487
  values.push(target);
440
- return isSQLite ? "?" : `$${values.length}`;
488
+ return fmt.placeholderAt(values.length);
441
489
  }
442
490
  if (ast === "field")
443
- return (0, exports.sqlsanitize)(field);
491
+ return sqlsanitize(field);
444
492
  const { type, name, args } = ast;
445
493
  switch (type) {
446
494
  case "SqlFun":
447
- return `${(0, exports.sqlsanitize)(name)}(${args.map(ppOp).join(",")})`;
495
+ return `${sqlsanitize(name)}(${args.map(ppOp).join(",")})`;
448
496
  case "SqlBinOp":
449
497
  const [arg1, arg2] = args;
450
498
  return `${ppOp(arg1)}${validOp(name)}${ppOp(arg2)}`;
@@ -453,21 +501,20 @@ const getOperatorOrder = ({ operator, target, field, }, values, isSQLite) => {
453
501
  };
454
502
  return ppOp(operator);
455
503
  };
456
- const orderByIsObject = (object) => {
504
+ export const orderByIsObject = (object) => {
457
505
  return object && object.distance;
458
506
  };
459
- exports.orderByIsObject = orderByIsObject;
460
- const orderByIsOperator = (object) => {
507
+ export const orderByIsOperator = (object) => {
461
508
  return object && object.operator && typeof object.operator !== "string";
462
509
  };
463
- exports.orderByIsOperator = orderByIsOperator;
464
510
  /**
465
- * @param {object} selopts
511
+ * Build the "order by / limit / offset / for update" tail of a select for a
512
+ * given dialect. Only fmt.placeholderAt is used (rare operator-orderBy case).
466
513
  * @returns {string}
467
514
  */
468
- const mkSelectOptions = (selopts, values, isSQLite) => {
515
+ export const mkSelectOptionsForDialect = (selopts, values, fmt) => {
469
516
  const orderby = selopts.orderBy === "RANDOM()"
470
- ? "order by RANDOM()"
517
+ ? `order by ${fmt.randomOrderExpr?.() ?? "RANDOM()"}`
471
518
  : selopts.orderBy &&
472
519
  typeof selopts.orderBy === "object" &&
473
520
  "distance" in selopts.orderBy
@@ -475,22 +522,26 @@ const mkSelectOptions = (selopts, values, isSQLite) => {
475
522
  : selopts.orderBy &&
476
523
  typeof selopts.orderBy === "string" &&
477
524
  selopts.nocase
478
- ? `order by lower(${quote((0, exports.sqlsanitizeAllowDots)(selopts.orderBy))})${selopts.orderDesc ? " DESC" : ""}`
525
+ ? `order by lower(${quote(sqlsanitizeAllowDots(selopts.orderBy))})${selopts.orderDesc ? " DESC" : ""}`
479
526
  : selopts.orderBy && typeof selopts.orderBy === "string"
480
- ? `order by ${quote((0, exports.sqlsanitizeAllowDots)(selopts.orderBy))}${selopts.orderDesc ? " DESC" : ""}`
527
+ ? `order by ${quote(sqlsanitizeAllowDots(selopts.orderBy))}${selopts.orderDesc ? " DESC" : ""}`
481
528
  : selopts.orderBy &&
482
529
  typeof selopts.orderBy === "object" &&
483
530
  "operator" in selopts.orderBy &&
484
531
  typeof selopts.orderBy.operator === "object"
485
- ? `order by ${getOperatorOrder(selopts.orderBy, values, isSQLite)}`
532
+ ? `order by ${getOperatorOrder(selopts.orderBy, values, fmt)}`
486
533
  : "";
487
534
  const limit = selopts.limit ? `limit ${toInt(selopts.limit)}` : "";
488
535
  const offset = selopts.offset ? `offset ${toInt(selopts.offset)}` : "";
489
536
  const forupdate = selopts.forupdate ? "FOR UPDATE" : "";
490
537
  return [orderby, limit, offset, forupdate].filter((s) => s).join(" ");
491
538
  };
492
- exports.mkSelectOptions = mkSelectOptions;
493
- const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
539
+ /**
540
+ * @param {object} selopts
541
+ * @returns {string}
542
+ */
543
+ export const mkSelectOptions = (selopts, values, isSQLite) => mkSelectOptionsForDialect(selopts, values, isSQLite ? sqlitePlaceHolderStack() : defaultDialectFactory());
544
+ export const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
494
545
  if (!inputWhere)
495
546
  return {};
496
547
  const whereObj = {};
@@ -498,17 +549,17 @@ const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
498
549
  if (k === "_fts")
499
550
  whereObj[k] = { table: tablePrefix, ...inputWhere[k] };
500
551
  else if (k === "not") {
501
- whereObj.not = (0, exports.prefixFieldsInWhere)(inputWhere[k], tablePrefix);
552
+ whereObj.not = prefixFieldsInWhere(inputWhere[k], tablePrefix);
502
553
  }
503
554
  else if (k === "or") {
504
555
  whereObj.or = Array.isArray(inputWhere[k])
505
- ? inputWhere[k].map((w) => (0, exports.prefixFieldsInWhere)(w, tablePrefix))
506
- : [(0, exports.prefixFieldsInWhere)(inputWhere[k], tablePrefix)];
556
+ ? inputWhere[k].map((w) => prefixFieldsInWhere(w, tablePrefix))
557
+ : [prefixFieldsInWhere(inputWhere[k], tablePrefix)];
507
558
  }
508
559
  else if (k === "and") {
509
560
  whereObj.and = Array.isArray(inputWhere[k])
510
- ? inputWhere[k].map((w) => (0, exports.prefixFieldsInWhere)(w, tablePrefix))
511
- : (0, exports.prefixFieldsInWhere)(inputWhere[k], tablePrefix);
561
+ ? inputWhere[k].map((w) => prefixFieldsInWhere(w, tablePrefix))
562
+ : prefixFieldsInWhere(inputWhere[k], tablePrefix);
512
563
  }
513
564
  else if (k === "eq") {
514
565
  whereObj[k] = inputWhere[k]; // TODO check for fieldnames
@@ -518,18 +569,15 @@ const prefixFieldsInWhere = (inputWhere, tablePrefix) => {
518
569
  });
519
570
  return whereObj;
520
571
  };
521
- exports.prefixFieldsInWhere = prefixFieldsInWhere;
522
- const sqlFun = (name, ...args) => ({
572
+ export const sqlFun = (name, ...args) => ({
523
573
  type: "SqlFun",
524
574
  name,
525
575
  args,
526
576
  });
527
- exports.sqlFun = sqlFun;
528
- const sqlBinOp = (name, ...args) => ({
577
+ export const sqlBinOp = (name, ...args) => ({
529
578
  type: "SqlBinOp",
530
579
  name,
531
580
  args,
532
581
  });
533
- exports.sqlBinOp = sqlBinOp;
534
- exports.dbCommonModulePath = __dirname;
582
+ export const dbCommonModulePath = import.meta.dirname;
535
583
  //# sourceMappingURL=internal.js.map