@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/dbtypes.d.ts +11 -2
- package/dist/dbtypes.d.ts.map +1 -1
- package/dist/dbtypes.js +1 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -47
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +50 -7
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +192 -146
- package/dist/internal.js.map +1 -1
- package/dist/internal.test.js +8 -4
- package/dist/internal.test.js.map +1 -1
- package/dist/multi-tenant.d.ts +1 -0
- package/dist/multi-tenant.d.ts.map +1 -1
- package/dist/multi-tenant.js +13 -22
- package/dist/multi-tenant.js.map +1 -1
- package/dist/sqlite-commons.d.ts +3 -3
- package/dist/sqlite-commons.d.ts.map +1 -1
- package/dist/sqlite-commons.js +40 -60
- package/dist/sqlite-commons.js.map +1 -1
- package/dist/tenants.d.ts +2 -2
- package/dist/tenants.d.ts.map +1 -1
- package/dist/tenants.js +8 -3
- package/dist/tenants.js.map +1 -1
- package/dist/test_expect.d.ts +83 -0
- package/dist/test_expect.d.ts.map +1 -0
- package/dist/test_expect.js +401 -0
- package/dist/test_expect.js.map +1 -0
- package/dist/types.d.ts +23 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -2
- package/package.json +3 -14
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
|
-
|
|
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 ?
|
|
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 ?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
push(x)
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
? `"${
|
|
98
|
-
: `"${
|
|
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}".` : ""}"${
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
* Where FTS (Search)
|
|
115
|
-
* @param {object} v
|
|
116
|
-
* @param {string} i
|
|
186
|
+
export /**
|
|
187
|
+
*
|
|
117
188
|
* @param {boolean} is_sqlite
|
|
118
|
-
* @
|
|
119
|
-
|
|
120
|
-
const
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
229
|
+
const alias = quote(sqlsanitize(`${table}SubJ${i}`));
|
|
174
230
|
if (i === 0) {
|
|
175
|
-
selectParts.push(`from ${schema}${quote(
|
|
176
|
-
whereObj =
|
|
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(
|
|
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(
|
|
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}.${
|
|
246
|
+
inColumn = quote(`${lastAlias}.${sqlsanitize(fkey)}`);
|
|
191
247
|
}
|
|
192
248
|
else {
|
|
193
|
-
selectParts.push(`join ${schema}${quote(
|
|
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(
|
|
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(
|
|
238
|
-
: phs.push(v) + (typeof v === "string" ?
|
|
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
|
-
?
|
|
314
|
+
? phs.ftsWhereClause(v)
|
|
271
315
|
: typeof (v || {}).not !== "undefined" && v.not.in
|
|
272
|
-
?
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
?
|
|
277
|
-
?
|
|
278
|
-
:
|
|
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
|
-
?
|
|
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(
|
|
350
|
+
? `${quote(sqlsanitizeAllowDots(k))} ${phs.like()} ${phs.push(v.ilike)}`
|
|
305
351
|
: typeof (v || {}).ilike !== "undefined"
|
|
306
|
-
? `${quote(
|
|
352
|
+
? `${quote(sqlsanitizeAllowDots(k))} ${phs.like()} '%' || ${phs.push(v.ilike)} || '%'`
|
|
307
353
|
: v instanceof RegExp ||
|
|
308
354
|
v?.constructor?.name === "RegExp"
|
|
309
|
-
? `${quote(
|
|
355
|
+
? `${quote(sqlsanitizeAllowDots(k))} ${phs.regexOperator()} ${phs.push(v.source)}`
|
|
310
356
|
: typeof (v || {}).gt !== "undefined" &&
|
|
311
357
|
typeof (v || {}).lt !== "undefined"
|
|
312
|
-
? `${
|
|
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
|
-
? `${
|
|
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
|
-
? `${
|
|
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
|
-
?
|
|
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(
|
|
374
|
+
? `${quote(sqlsanitizeAllowDots(k))} is null`
|
|
329
375
|
: k === "not"
|
|
330
376
|
? `not (${typeof v === "symbol" ? v.description : phs.push(v)})`
|
|
331
|
-
: `${quote(
|
|
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.
|
|
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.
|
|
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
|
-
*
|
|
376
|
-
* @param
|
|
377
|
-
* @param
|
|
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
|
|
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(
|
|
427
|
+
? "where " + wheres.map(whereClause(dialect)).join(" and ")
|
|
388
428
|
: "";
|
|
389
|
-
const values =
|
|
429
|
+
const values = dialect.getValues();
|
|
390
430
|
return { where, values };
|
|
391
431
|
};
|
|
392
|
-
|
|
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 `((${
|
|
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,
|
|
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
|
|
485
|
+
return fmt.placeholderAt(values.length);
|
|
440
486
|
}
|
|
441
487
|
if (ast === "field")
|
|
442
|
-
return
|
|
488
|
+
return sqlsanitize(field);
|
|
443
489
|
const { type, name, args } = ast;
|
|
444
490
|
switch (type) {
|
|
445
491
|
case "SqlFun":
|
|
446
|
-
return `${
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
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(
|
|
522
|
+
? `order by lower(${quote(sqlsanitizeAllowDots(selopts.orderBy))})${selopts.orderDesc ? " DESC" : ""}`
|
|
478
523
|
: selopts.orderBy && typeof selopts.orderBy === "string"
|
|
479
|
-
? `order by ${quote(
|
|
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,
|
|
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
|
-
|
|
492
|
-
|
|
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 =
|
|
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) =>
|
|
505
|
-
: [
|
|
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) =>
|
|
510
|
-
:
|
|
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
|
-
|
|
521
|
-
const sqlFun = (name, ...args) => ({
|
|
569
|
+
export const sqlFun = (name, ...args) => ({
|
|
522
570
|
type: "SqlFun",
|
|
523
571
|
name,
|
|
524
572
|
args,
|
|
525
573
|
});
|
|
526
|
-
|
|
527
|
-
const sqlBinOp = (name, ...args) => ({
|
|
574
|
+
export const sqlBinOp = (name, ...args) => ({
|
|
528
575
|
type: "SqlBinOp",
|
|
529
576
|
name,
|
|
530
577
|
args,
|
|
531
578
|
});
|
|
532
|
-
|
|
533
|
-
exports.dbCommonModulePath = __dirname;
|
|
579
|
+
export const dbCommonModulePath = import.meta.dirname;
|
|
534
580
|
//# sourceMappingURL=internal.js.map
|