@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/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 +51 -7
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +195 -147
- package/dist/internal.js.map +1 -1
- package/dist/internal.test.js +9 -5
- 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 +89 -0
- package/dist/test_expect.d.ts.map +1 -0
- package/dist/test_expect.js +530 -0
- package/dist/test_expect.js.map +1 -0
- package/dist/types.d.ts +94 -5
- 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,25 +416,27 @@ 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
|
-
|
|
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 =
|
|
430
|
+
const values = dialect.getValues();
|
|
391
431
|
return { where, values };
|
|
392
432
|
};
|
|
393
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
488
|
+
return fmt.placeholderAt(values.length);
|
|
441
489
|
}
|
|
442
490
|
if (ast === "field")
|
|
443
|
-
return
|
|
491
|
+
return sqlsanitize(field);
|
|
444
492
|
const { type, name, args } = ast;
|
|
445
493
|
switch (type) {
|
|
446
494
|
case "SqlFun":
|
|
447
|
-
return `${
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
515
|
+
export const mkSelectOptionsForDialect = (selopts, values, fmt) => {
|
|
469
516
|
const orderby = selopts.orderBy === "RANDOM()"
|
|
470
|
-
?
|
|
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(
|
|
525
|
+
? `order by lower(${quote(sqlsanitizeAllowDots(selopts.orderBy))})${selopts.orderDesc ? " DESC" : ""}`
|
|
479
526
|
: selopts.orderBy && typeof selopts.orderBy === "string"
|
|
480
|
-
? `order by ${quote(
|
|
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,
|
|
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
|
-
|
|
493
|
-
|
|
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 =
|
|
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) =>
|
|
506
|
-
: [
|
|
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) =>
|
|
511
|
-
:
|
|
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
|
-
|
|
522
|
-
const sqlFun = (name, ...args) => ({
|
|
572
|
+
export const sqlFun = (name, ...args) => ({
|
|
523
573
|
type: "SqlFun",
|
|
524
574
|
name,
|
|
525
575
|
args,
|
|
526
576
|
});
|
|
527
|
-
|
|
528
|
-
const sqlBinOp = (name, ...args) => ({
|
|
577
|
+
export const sqlBinOp = (name, ...args) => ({
|
|
529
578
|
type: "SqlBinOp",
|
|
530
579
|
name,
|
|
531
580
|
args,
|
|
532
581
|
});
|
|
533
|
-
|
|
534
|
-
exports.dbCommonModulePath = __dirname;
|
|
582
|
+
export const dbCommonModulePath = import.meta.dirname;
|
|
535
583
|
//# sourceMappingURL=internal.js.map
|