@graffy/pg 0.15.13 → 0.15.14-alpha.4
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/index.cjs +138 -136
- package/index.mjs +138 -136
- package/package.json +4 -4
- package/types/Db.d.ts +1 -0
- package/types/filter/getSql.d.ts +1 -3
- package/types/index.d.ts +2 -1
- package/types/sql/clauses.d.ts +2 -2
package/index.cjs
CHANGED
|
@@ -53,8 +53,7 @@ const valid = {
|
|
|
53
53
|
$all: true,
|
|
54
54
|
$has: true,
|
|
55
55
|
$cts: true,
|
|
56
|
-
$ctd: true
|
|
57
|
-
$ovl: true
|
|
56
|
+
$ctd: true
|
|
58
57
|
};
|
|
59
58
|
const inverse = {
|
|
60
59
|
$eq: "$neq",
|
|
@@ -67,10 +66,8 @@ const inverse = {
|
|
|
67
66
|
$lte: "$gt"
|
|
68
67
|
};
|
|
69
68
|
function getAst(filter) {
|
|
70
|
-
counter = 0;
|
|
71
69
|
return simplify(construct(filter));
|
|
72
70
|
}
|
|
73
|
-
let counter;
|
|
74
71
|
function construct(node, prop, op) {
|
|
75
72
|
if (!node || typeof node !== "object" || prop && op) {
|
|
76
73
|
if (op && prop)
|
|
@@ -91,14 +88,6 @@ function construct(node, prop, op) {
|
|
|
91
88
|
if (key === "$not") {
|
|
92
89
|
return [key, construct(val, prop, op)];
|
|
93
90
|
}
|
|
94
|
-
if (key === "$any" || key === "$all") {
|
|
95
|
-
const elkey = "el$" + counter++;
|
|
96
|
-
return [key, prop, elkey, construct(val, elkey)];
|
|
97
|
-
}
|
|
98
|
-
if (key === "$has") {
|
|
99
|
-
const elkey = "el$" + counter++;
|
|
100
|
-
return [key, prop, elkey, construct(val, elkey)[1]];
|
|
101
|
-
}
|
|
102
91
|
if (key[0] === "$") {
|
|
103
92
|
if (!valid[key])
|
|
104
93
|
throw Error("pgast.invalid_op:" + key);
|
|
@@ -123,10 +112,21 @@ function simplify(node) {
|
|
|
123
112
|
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
124
113
|
} else if (op === "$not") {
|
|
125
114
|
node[1] = simplify(node[1]);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
115
|
+
}
|
|
116
|
+
if (op === "$and") {
|
|
117
|
+
if (!node[1].length)
|
|
118
|
+
return true;
|
|
119
|
+
if (node[1].includes(false))
|
|
120
|
+
return false;
|
|
121
|
+
node[1] = node[1].filter((item) => item !== true);
|
|
122
|
+
} else if (op === "$or") {
|
|
123
|
+
if (!node[1].length)
|
|
124
|
+
return false;
|
|
125
|
+
if (node[1].includes(true))
|
|
126
|
+
return true;
|
|
127
|
+
node[1] = node[1].filter((item) => item !== false);
|
|
128
|
+
} else if (op === "$not" && typeof node[1] === "boolean") {
|
|
129
|
+
return !node[1];
|
|
130
130
|
}
|
|
131
131
|
if (op === "$or") {
|
|
132
132
|
const { eqmap, noneq, change } = node[1].reduce((acc, item) => {
|
|
@@ -148,115 +148,16 @@ function simplify(node) {
|
|
|
148
148
|
];
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
-
if (op === "$and" || op === "$or") {
|
|
152
|
-
|
|
153
|
-
throw Error("pgast.expected_children:" + op);
|
|
154
|
-
return node[1].length === 1 ? node[1][0] : node;
|
|
151
|
+
if ((op === "$and" || op === "$or") && node[1].length === 1) {
|
|
152
|
+
return node[1][0];
|
|
155
153
|
}
|
|
156
154
|
if (op === "$not") {
|
|
157
155
|
const [subop, ...subargs] = node[1];
|
|
158
156
|
const invop = inverse[subop];
|
|
159
157
|
return invop ? [invop, ...subargs] : node;
|
|
160
158
|
}
|
|
161
|
-
if (op === "$any" || op === "$all") {
|
|
162
|
-
const [_, list, elkey, subnode] = node;
|
|
163
|
-
const [subop, elk, val] = subnode;
|
|
164
|
-
return (subop === "$eq" || subop === "$in") && elkey === elk ? [op === "$any" ? "$ovl" : "$ctd", list, subop === "$eq" ? [val] : val] : node;
|
|
165
|
-
}
|
|
166
|
-
if (op === "$has") {
|
|
167
|
-
const [_, list, elkey, limbs] = node;
|
|
168
|
-
return limbs.every(([subop, elk]) => subop === "$eq" && elk === elkey) ? ["$cts", list, limbs.map(([_op, _prop, val]) => val)] : node;
|
|
169
|
-
}
|
|
170
159
|
return node;
|
|
171
160
|
}
|
|
172
|
-
function defaultColumnType(_) {
|
|
173
|
-
return "any";
|
|
174
|
-
}
|
|
175
|
-
function getCompatibleTypes(value) {
|
|
176
|
-
if (value === null)
|
|
177
|
-
return "any";
|
|
178
|
-
if (Array.isArray(value))
|
|
179
|
-
return "array";
|
|
180
|
-
if (typeof value === "object")
|
|
181
|
-
return "jsonb";
|
|
182
|
-
if (typeof value === "number")
|
|
183
|
-
return "numeric";
|
|
184
|
-
if (typeof value === "string")
|
|
185
|
-
return "text";
|
|
186
|
-
}
|
|
187
|
-
function getSql(filter, getLookupSql, getColumnType = defaultColumnType) {
|
|
188
|
-
function lookup2(string, type) {
|
|
189
|
-
if (string.substr(0, 3) === "el$")
|
|
190
|
-
return sql__default["default"]`"${sql.raw(string)}"`;
|
|
191
|
-
return getLookupSql(string, type);
|
|
192
|
-
}
|
|
193
|
-
function binop(op, left, right) {
|
|
194
|
-
const lType = left.substr(0, 3) === "el$" ? "any" : getColumnType(left);
|
|
195
|
-
const rType = getCompatibleTypes(right);
|
|
196
|
-
if (lType === "any" || rType === "any" || rType === lType) {
|
|
197
|
-
return sql__default["default"]`${lookup2(left)} ${sql.raw(op)} ${right}`;
|
|
198
|
-
} else {
|
|
199
|
-
return sql__default["default"]`(${lookup2(left, rType)})::${sql.raw(rType)} ${sql.raw(op)} ${right}`;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function getNodeSql(ast) {
|
|
203
|
-
switch (ast[0]) {
|
|
204
|
-
case "$eq":
|
|
205
|
-
if (ast[2] === null)
|
|
206
|
-
return sql__default["default"]`${lookup2(ast[1])} IS NULL`;
|
|
207
|
-
return binop("=", ast[1], ast[2]);
|
|
208
|
-
case "$neq":
|
|
209
|
-
if (ast[2] === null)
|
|
210
|
-
return sql__default["default"]`${lookup2(ast[1])} IS NOT NULL`;
|
|
211
|
-
return binop("<>", ast[1], ast[2]);
|
|
212
|
-
case "$lt":
|
|
213
|
-
return binop("<", ast[1], ast[2]);
|
|
214
|
-
case "$lte":
|
|
215
|
-
return binop("<=", ast[1], ast[2]);
|
|
216
|
-
case "$gt":
|
|
217
|
-
return binop(">", ast[1], ast[2]);
|
|
218
|
-
case "$gte":
|
|
219
|
-
return binop(">=", ast[1], ast[2]);
|
|
220
|
-
case "$re":
|
|
221
|
-
return binop("~", ast[1], ast[2]);
|
|
222
|
-
case "$ire":
|
|
223
|
-
return binop("~*", ast[1], ast[2]);
|
|
224
|
-
case "$in":
|
|
225
|
-
return sql__default["default"]`${lookup2(ast[1])} IN (${sql.join(ast[2])})`;
|
|
226
|
-
case "$nin":
|
|
227
|
-
return sql__default["default"]`${lookup2(ast[1])} NOT IN (${sql.join(ast[2])})`;
|
|
228
|
-
case "$cts":
|
|
229
|
-
return sql__default["default"]`${lookup2(ast[1])} @> ${ast[2]}`;
|
|
230
|
-
case "$ctd":
|
|
231
|
-
return sql__default["default"]`${lookup2(ast[1])} <@ ${ast[2]}`;
|
|
232
|
-
case "$ovl":
|
|
233
|
-
switch (getColumnType(ast[1])) {
|
|
234
|
-
case "jsonb":
|
|
235
|
-
case "any":
|
|
236
|
-
return sql__default["default"]`${lookup2(ast[1])} ?| ${Array.isArray(ast[2]) ? ast[2] : Object.keys(ast[2])}`;
|
|
237
|
-
case "array":
|
|
238
|
-
return sql__default["default"]`${lookup2(ast[1])} && ${ast[2]}`;
|
|
239
|
-
default:
|
|
240
|
-
throw Error("pg.getSql_ovl_unknown_column_type");
|
|
241
|
-
}
|
|
242
|
-
case "$and":
|
|
243
|
-
return sql__default["default"]`(${sql.join(ast[1].map((node) => getNodeSql(node)), `) AND (`)})`;
|
|
244
|
-
case "$or":
|
|
245
|
-
return sql__default["default"]`(${sql.join(ast[1].map((node) => getNodeSql(node)), `) OR (`)})`;
|
|
246
|
-
case "$not":
|
|
247
|
-
return sql__default["default"]`NOT (${getNodeSql(ast[1])})`;
|
|
248
|
-
case "$any":
|
|
249
|
-
return sql__default["default"]`(SELECT bool_or(${getNodeSql(ast[3])}) FROM UNNEST(${lookup2(ast[1])}) ${lookup2(ast[2])})`;
|
|
250
|
-
case "$all":
|
|
251
|
-
return sql__default["default"]`(SELECT bool_and(${getNodeSql(ast[3])}) FROM UNNEST(${lookup2(ast[1])}) ${lookup2(ast[2])})`;
|
|
252
|
-
case "$has":
|
|
253
|
-
return sql__default["default"]`(SELECT bool_or(${sql.join(ast[3].map((node) => getNodeSql(node)), `) AND bool_or(`)}) FROM UNNEST(${lookup2(ast[1])}) ${lookup2(ast[2])})`;
|
|
254
|
-
default:
|
|
255
|
-
throw Error("pg.getSql_unknown_operator: " + ast[0]);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return getNodeSql(getAst(filter));
|
|
259
|
-
}
|
|
260
161
|
const nowTimestamp = sql__default["default"]`cast(extract(epoch from now()) as integer)`;
|
|
261
162
|
const getJsonBuildObject = (variadic) => {
|
|
262
163
|
const args = sql.join(Object.entries(variadic).map(([name, value]) => {
|
|
@@ -271,14 +172,9 @@ const getJsonBuildValue = (value) => {
|
|
|
271
172
|
return sql__default["default"]`${value}::text`;
|
|
272
173
|
return sql__default["default"]`${JSON.stringify(stripAttributes(value))}::jsonb`;
|
|
273
174
|
};
|
|
274
|
-
const lookup = (prop
|
|
175
|
+
const lookup = (prop) => {
|
|
275
176
|
const [prefix, ...suffix] = common.encodePath(prop);
|
|
276
|
-
|
|
277
|
-
return suffix.length ? sql__default["default"]`"${sql.raw(prefix)}" ${op} ${suffix}` : sql__default["default"]`"${sql.raw(prefix)}"`;
|
|
278
|
-
};
|
|
279
|
-
const getType = (prop) => {
|
|
280
|
-
const [_prefix, ...suffix] = common.encodePath(prop);
|
|
281
|
-
return suffix.length ? "jsonb" : "any";
|
|
177
|
+
return suffix.length ? sql__default["default"]`"${sql.raw(prefix)}" #> ${suffix}` : sql__default["default"]`"${sql.raw(prefix)}"`;
|
|
282
178
|
};
|
|
283
179
|
const aggSql = {
|
|
284
180
|
$sum: (prop) => sql__default["default"]`sum((${lookup(prop)})::numeric)`,
|
|
@@ -306,19 +202,39 @@ const getSelectCols = (table, projection = null) => {
|
|
|
306
202
|
}
|
|
307
203
|
return sql__default["default"]`jsonb_build_object(${sql.join(sqls, ", ")})`;
|
|
308
204
|
};
|
|
205
|
+
function vertexSql(array) {
|
|
206
|
+
return sql__default["default"]`array[${sql.join(array.map((num) => num === Infinity ? sql__default["default"]`'Infinity'` : num === -Infinity ? sql__default["default"]`'-Infinity'` : num))}]::float8[]`;
|
|
207
|
+
}
|
|
208
|
+
function castValue$1(value, type, name, isPut) {
|
|
209
|
+
if (!type)
|
|
210
|
+
throw Error("pg.write_no_column " + name);
|
|
211
|
+
if (value instanceof sql.Sql)
|
|
212
|
+
return value;
|
|
213
|
+
if (value === null)
|
|
214
|
+
return sql__default["default"]`NULL`;
|
|
215
|
+
if (type === "jsonb") {
|
|
216
|
+
console.log("jsonb", value, type, name);
|
|
217
|
+
return isPut ? JSON.stringify(stripAttributes(value)) : sql__default["default"]`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})`;
|
|
218
|
+
}
|
|
219
|
+
if (type === "cube") {
|
|
220
|
+
if (!Array.isArray(value) || !value.length || Array.isArray(value[0]) && value.length !== 2) {
|
|
221
|
+
throw Error("pg.castValue_bad_cube" + JSON.stringify(value));
|
|
222
|
+
}
|
|
223
|
+
return Array.isArray(value[0]) ? sql__default["default"]`cube(${vertexSql(value[0])}, ${vertexSql(value[1])})` : sql__default["default"]`cube(${vertexSql(value)})`;
|
|
224
|
+
}
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
309
227
|
const getInsert = (row, options) => {
|
|
310
228
|
const cols = [];
|
|
311
229
|
const vals = [];
|
|
312
|
-
Object.entries(row).filter(([
|
|
230
|
+
Object.entries(row).filter(([col]) => col !== options.verCol && col[0] !== "$").concat([[options.verCol, nowTimestamp]]).forEach(([col, val]) => {
|
|
313
231
|
cols.push(sql__default["default"]`"${sql.raw(col)}"`);
|
|
314
|
-
vals.push(val
|
|
232
|
+
vals.push(castValue$1(val, options.schema.types[col], col, row.$put));
|
|
315
233
|
});
|
|
316
234
|
return { cols: sql.join(cols, ", "), vals: sql.join(vals, ", ") };
|
|
317
235
|
};
|
|
318
236
|
const getUpdates = (row, options) => {
|
|
319
|
-
return sql.join(Object.entries(row).filter(([
|
|
320
|
-
return sql__default["default"]`"${sql.raw(name)}" = ${value instanceof sql.Sql || typeof value !== "object" || !value ? value : !value.$put ? sql__default["default"]`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})` : sql__default["default"]`${JSON.stringify(stripAttributes(value))}::jsonb`}`;
|
|
321
|
-
}).concat(sql__default["default"]`"${sql.raw(options.verCol)}" = ${nowTimestamp}`), ", ");
|
|
237
|
+
return sql.join(Object.entries(row).filter(([col]) => col !== options.idCol && col[0] !== "$").map(([col, val]) => sql__default["default"]`"${sql.raw(col)}" = ${castValue$1(val, options.schema.types[col], col, row.$put)}`).concat(sql__default["default"]`"${sql.raw(options.verCol)}" = ${nowTimestamp}`), ", ");
|
|
322
238
|
};
|
|
323
239
|
function getJsonUpdate(_a, col, path) {
|
|
324
240
|
var _b = _a, { $put } = _b, object = __objRest(_b, ["$put"]);
|
|
@@ -345,6 +261,68 @@ function stripAttributes(object) {
|
|
|
345
261
|
return out;
|
|
346
262
|
}, {});
|
|
347
263
|
}
|
|
264
|
+
const opSql = {
|
|
265
|
+
$and: `AND`,
|
|
266
|
+
$or: `OR`,
|
|
267
|
+
$not: sql__default["default"]`NOT`,
|
|
268
|
+
$eq: sql__default["default"]`=`,
|
|
269
|
+
$neq: sql__default["default"]`<>`,
|
|
270
|
+
$in: sql__default["default"]`IN`,
|
|
271
|
+
$nin: sql__default["default"]`NOT IN`,
|
|
272
|
+
$lt: sql__default["default"]`<`,
|
|
273
|
+
$lte: sql__default["default"]`<=`,
|
|
274
|
+
$gt: sql__default["default"]`>`,
|
|
275
|
+
$gte: sql__default["default"]`>=`,
|
|
276
|
+
$re: sql__default["default"]`~`,
|
|
277
|
+
$ire: sql__default["default"]`~*`,
|
|
278
|
+
$cts: sql__default["default"]`@>`,
|
|
279
|
+
$ctd: sql__default["default"]`<@`
|
|
280
|
+
};
|
|
281
|
+
function castValue(value, type, op) {
|
|
282
|
+
if (value === null && op === "$eq")
|
|
283
|
+
return sql__default["default"]`IS NULL`;
|
|
284
|
+
if (value === null && op === "$neq")
|
|
285
|
+
return sql__default["default"]`IS NOT NULL`;
|
|
286
|
+
const sqlOp = opSql[op];
|
|
287
|
+
if (!sqlOp)
|
|
288
|
+
throw Error("pg.getSql_unknown_operator " + op);
|
|
289
|
+
if (op === "$in" || op === "$nin") {
|
|
290
|
+
return sql__default["default"]`${sqlOp} (${sql.join(value)})`;
|
|
291
|
+
}
|
|
292
|
+
if (type === "jsonb") {
|
|
293
|
+
return sql__default["default"]`${sqlOp} ${JSON.stringify(value)}::jsonb`;
|
|
294
|
+
}
|
|
295
|
+
if (type === "cube") {
|
|
296
|
+
if (!Array.isArray(value) || !value.length || Array.isArray(value[0]) && value.length !== 2) {
|
|
297
|
+
throw Error("pg.castValue_bad_cube" + JSON.stringify(value));
|
|
298
|
+
}
|
|
299
|
+
return Array.isArray(value[0]) ? sql__default["default"]`${sqlOp} cube(${vertexSql(value[0])}, ${vertexSql(value[1])})` : sql__default["default"]`${sqlOp} cube(${vertexSql(value)})`;
|
|
300
|
+
}
|
|
301
|
+
return sql__default["default"]`${sqlOp} ${value}`;
|
|
302
|
+
}
|
|
303
|
+
function getSql(filter, options) {
|
|
304
|
+
function getNodeSql(ast) {
|
|
305
|
+
if (typeof ast === "boolean")
|
|
306
|
+
return ast;
|
|
307
|
+
const op = ast[0];
|
|
308
|
+
if (op === "$and" || op === "$or") {
|
|
309
|
+
return sql__default["default"]`(${sql.join(ast[1].map((node) => getNodeSql(node)), `) ${opSql[op]} (`)})`;
|
|
310
|
+
} else if (op === "$not") {
|
|
311
|
+
return sql__default["default"]`${opSql[op]} (${getNodeSql(ast[1])})`;
|
|
312
|
+
}
|
|
313
|
+
const [prefix, ...suffix] = common.encodePath(ast[1]);
|
|
314
|
+
const { types } = options.schema;
|
|
315
|
+
if (!types[prefix])
|
|
316
|
+
throw Error("pg.no_column " + prefix);
|
|
317
|
+
if (suffix.length && types[prefix] !== "jsonb") {
|
|
318
|
+
throw Error("pg.lookup_not_jsonb " + prefix);
|
|
319
|
+
}
|
|
320
|
+
const [lhs, type] = suffix.length ? [sql__default["default"]`"${sql.raw(prefix)}" #> ${suffix}`, "jsonb"] : [sql__default["default"]`"${sql.raw(prefix)}"`, types[prefix]];
|
|
321
|
+
const rhs = castValue(ast[2], type, op);
|
|
322
|
+
return sql__default["default"]`${lhs} ${rhs}`;
|
|
323
|
+
}
|
|
324
|
+
return getNodeSql(getAst(filter));
|
|
325
|
+
}
|
|
348
326
|
const getIdMeta = ({ idCol }) => getJsonBuildObject({
|
|
349
327
|
$key: sql__default["default"]`"${sql.raw(idCol)}"`,
|
|
350
328
|
$ver: nowTimestamp
|
|
@@ -366,13 +344,13 @@ function getArgSql(_c, options) {
|
|
|
366
344
|
throw Error("pg_arg.order_and_group_unsupported in " + prefix);
|
|
367
345
|
}
|
|
368
346
|
const meta = (key2) => $group ? getAggMeta(key2, $group) : getArgMeta(key2, prefix, idCol);
|
|
369
|
-
const groupCols = Array.isArray($group) && $group.length
|
|
347
|
+
const groupCols = Array.isArray($group) && $group.length && $group.map(lookup);
|
|
370
348
|
const group = groupCols ? sql.join(groupCols, ", ") : void 0;
|
|
371
349
|
const hasRangeArg = $before || $after || $since || $until || $first || $last || $all || $order;
|
|
372
350
|
let key;
|
|
373
351
|
const where = [];
|
|
374
352
|
if (!common.isEmpty(filter)) {
|
|
375
|
-
where.push(getSql(filter,
|
|
353
|
+
where.push(getSql(filter, options));
|
|
376
354
|
key = sql__default["default"]`${JSON.stringify(filter)}::jsonb`;
|
|
377
355
|
}
|
|
378
356
|
if (!hasRangeArg)
|
|
@@ -533,24 +511,46 @@ class Db {
|
|
|
533
511
|
}
|
|
534
512
|
}
|
|
535
513
|
async readSql(sql2) {
|
|
536
|
-
|
|
537
|
-
result = result.rows.flat();
|
|
514
|
+
const result = (await this.query(sql2)).rows.flat();
|
|
538
515
|
log("Read result", result);
|
|
539
516
|
return result;
|
|
540
517
|
}
|
|
541
518
|
async writeSql(sql2) {
|
|
542
|
-
|
|
519
|
+
const res = await this.query(sql2);
|
|
543
520
|
log("Rows written", res.rowCount);
|
|
544
521
|
if (!res.rowCount) {
|
|
545
522
|
throw Error("pg.nothing_written " + sql2.text + " with " + sql2.values);
|
|
546
523
|
}
|
|
547
524
|
return res.rows[0][0];
|
|
548
525
|
}
|
|
526
|
+
async ensureSchema(tableOptions) {
|
|
527
|
+
if (tableOptions.schema)
|
|
528
|
+
return;
|
|
529
|
+
const { table } = tableOptions;
|
|
530
|
+
const types = (await this.query(sql__default["default"]`SELECT jsonb_object_agg(
|
|
531
|
+
column_name,
|
|
532
|
+
udt_name
|
|
533
|
+
)
|
|
534
|
+
FROM information_schema.columns
|
|
535
|
+
WHERE
|
|
536
|
+
table_name = ${table} AND
|
|
537
|
+
table_schema = (
|
|
538
|
+
SELECT table_schema
|
|
539
|
+
FROM information_schema.tables
|
|
540
|
+
WHERE table_name = ${table}
|
|
541
|
+
ORDER BY array_position(current_schemas(false)::text[], table_schema::text) ASC
|
|
542
|
+
LIMIT 1)`)).rows[0][0];
|
|
543
|
+
if (!types)
|
|
544
|
+
throw Error(`pg.missing_table ${table}`);
|
|
545
|
+
log("ensureSchema", types);
|
|
546
|
+
tableOptions.schema = { types };
|
|
547
|
+
}
|
|
549
548
|
async read(rootQuery, tableOptions) {
|
|
550
549
|
const idQueries = {};
|
|
551
550
|
const promises = [];
|
|
552
551
|
const results = [];
|
|
553
552
|
const { prefix } = tableOptions;
|
|
553
|
+
await this.ensureSchema(tableOptions);
|
|
554
554
|
const getByArgs = async (args, projection) => {
|
|
555
555
|
const result = await this.readSql(selectByArgs(args, projection, tableOptions));
|
|
556
556
|
const wrappedGraph = common.encodeGraph(common.wrapObject(result, prefix));
|
|
@@ -591,6 +591,7 @@ class Db {
|
|
|
591
591
|
}
|
|
592
592
|
async write(rootChange, tableOptions) {
|
|
593
593
|
const { prefix } = tableOptions;
|
|
594
|
+
await this.ensureSchema(tableOptions);
|
|
594
595
|
const change = common.unwrap(rootChange, prefix);
|
|
595
596
|
const sqls = change.map((node) => {
|
|
596
597
|
const arg = common.decodeArgs(node);
|
|
@@ -618,7 +619,7 @@ class Db {
|
|
|
618
619
|
return result;
|
|
619
620
|
}
|
|
620
621
|
}
|
|
621
|
-
const pg = ({ table, idCol, verCol, connection }) => (store) => {
|
|
622
|
+
const pg = ({ table, idCol, verCol, connection, schema }) => (store) => {
|
|
622
623
|
store.on("read", read);
|
|
623
624
|
store.on("write", write);
|
|
624
625
|
const prefix = store.path;
|
|
@@ -626,7 +627,8 @@ const pg = ({ table, idCol, verCol, connection }) => (store) => {
|
|
|
626
627
|
prefix,
|
|
627
628
|
table: table || prefix[prefix.length - 1] || "default",
|
|
628
629
|
idCol: idCol || "id",
|
|
629
|
-
verCol: verCol || "updatedAt"
|
|
630
|
+
verCol: verCol || "updatedAt",
|
|
631
|
+
schema
|
|
630
632
|
};
|
|
631
633
|
const defaultDb = new Db(connection);
|
|
632
634
|
function read(query, options, next) {
|
package/index.mjs
CHANGED
|
@@ -45,8 +45,7 @@ const valid = {
|
|
|
45
45
|
$all: true,
|
|
46
46
|
$has: true,
|
|
47
47
|
$cts: true,
|
|
48
|
-
$ctd: true
|
|
49
|
-
$ovl: true
|
|
48
|
+
$ctd: true
|
|
50
49
|
};
|
|
51
50
|
const inverse = {
|
|
52
51
|
$eq: "$neq",
|
|
@@ -59,10 +58,8 @@ const inverse = {
|
|
|
59
58
|
$lte: "$gt"
|
|
60
59
|
};
|
|
61
60
|
function getAst(filter) {
|
|
62
|
-
counter = 0;
|
|
63
61
|
return simplify(construct(filter));
|
|
64
62
|
}
|
|
65
|
-
let counter;
|
|
66
63
|
function construct(node, prop, op) {
|
|
67
64
|
if (!node || typeof node !== "object" || prop && op) {
|
|
68
65
|
if (op && prop)
|
|
@@ -83,14 +80,6 @@ function construct(node, prop, op) {
|
|
|
83
80
|
if (key === "$not") {
|
|
84
81
|
return [key, construct(val, prop, op)];
|
|
85
82
|
}
|
|
86
|
-
if (key === "$any" || key === "$all") {
|
|
87
|
-
const elkey = "el$" + counter++;
|
|
88
|
-
return [key, prop, elkey, construct(val, elkey)];
|
|
89
|
-
}
|
|
90
|
-
if (key === "$has") {
|
|
91
|
-
const elkey = "el$" + counter++;
|
|
92
|
-
return [key, prop, elkey, construct(val, elkey)[1]];
|
|
93
|
-
}
|
|
94
83
|
if (key[0] === "$") {
|
|
95
84
|
if (!valid[key])
|
|
96
85
|
throw Error("pgast.invalid_op:" + key);
|
|
@@ -115,10 +104,21 @@ function simplify(node) {
|
|
|
115
104
|
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
116
105
|
} else if (op === "$not") {
|
|
117
106
|
node[1] = simplify(node[1]);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
107
|
+
}
|
|
108
|
+
if (op === "$and") {
|
|
109
|
+
if (!node[1].length)
|
|
110
|
+
return true;
|
|
111
|
+
if (node[1].includes(false))
|
|
112
|
+
return false;
|
|
113
|
+
node[1] = node[1].filter((item) => item !== true);
|
|
114
|
+
} else if (op === "$or") {
|
|
115
|
+
if (!node[1].length)
|
|
116
|
+
return false;
|
|
117
|
+
if (node[1].includes(true))
|
|
118
|
+
return true;
|
|
119
|
+
node[1] = node[1].filter((item) => item !== false);
|
|
120
|
+
} else if (op === "$not" && typeof node[1] === "boolean") {
|
|
121
|
+
return !node[1];
|
|
122
122
|
}
|
|
123
123
|
if (op === "$or") {
|
|
124
124
|
const { eqmap, noneq, change } = node[1].reduce((acc, item) => {
|
|
@@ -140,115 +140,16 @@ function simplify(node) {
|
|
|
140
140
|
];
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
-
if (op === "$and" || op === "$or") {
|
|
144
|
-
|
|
145
|
-
throw Error("pgast.expected_children:" + op);
|
|
146
|
-
return node[1].length === 1 ? node[1][0] : node;
|
|
143
|
+
if ((op === "$and" || op === "$or") && node[1].length === 1) {
|
|
144
|
+
return node[1][0];
|
|
147
145
|
}
|
|
148
146
|
if (op === "$not") {
|
|
149
147
|
const [subop, ...subargs] = node[1];
|
|
150
148
|
const invop = inverse[subop];
|
|
151
149
|
return invop ? [invop, ...subargs] : node;
|
|
152
150
|
}
|
|
153
|
-
if (op === "$any" || op === "$all") {
|
|
154
|
-
const [_, list, elkey, subnode] = node;
|
|
155
|
-
const [subop, elk, val] = subnode;
|
|
156
|
-
return (subop === "$eq" || subop === "$in") && elkey === elk ? [op === "$any" ? "$ovl" : "$ctd", list, subop === "$eq" ? [val] : val] : node;
|
|
157
|
-
}
|
|
158
|
-
if (op === "$has") {
|
|
159
|
-
const [_, list, elkey, limbs] = node;
|
|
160
|
-
return limbs.every(([subop, elk]) => subop === "$eq" && elk === elkey) ? ["$cts", list, limbs.map(([_op, _prop, val]) => val)] : node;
|
|
161
|
-
}
|
|
162
151
|
return node;
|
|
163
152
|
}
|
|
164
|
-
function defaultColumnType(_) {
|
|
165
|
-
return "any";
|
|
166
|
-
}
|
|
167
|
-
function getCompatibleTypes(value) {
|
|
168
|
-
if (value === null)
|
|
169
|
-
return "any";
|
|
170
|
-
if (Array.isArray(value))
|
|
171
|
-
return "array";
|
|
172
|
-
if (typeof value === "object")
|
|
173
|
-
return "jsonb";
|
|
174
|
-
if (typeof value === "number")
|
|
175
|
-
return "numeric";
|
|
176
|
-
if (typeof value === "string")
|
|
177
|
-
return "text";
|
|
178
|
-
}
|
|
179
|
-
function getSql(filter, getLookupSql, getColumnType = defaultColumnType) {
|
|
180
|
-
function lookup2(string, type) {
|
|
181
|
-
if (string.substr(0, 3) === "el$")
|
|
182
|
-
return sql`"${raw(string)}"`;
|
|
183
|
-
return getLookupSql(string, type);
|
|
184
|
-
}
|
|
185
|
-
function binop(op, left, right) {
|
|
186
|
-
const lType = left.substr(0, 3) === "el$" ? "any" : getColumnType(left);
|
|
187
|
-
const rType = getCompatibleTypes(right);
|
|
188
|
-
if (lType === "any" || rType === "any" || rType === lType) {
|
|
189
|
-
return sql`${lookup2(left)} ${raw(op)} ${right}`;
|
|
190
|
-
} else {
|
|
191
|
-
return sql`(${lookup2(left, rType)})::${raw(rType)} ${raw(op)} ${right}`;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
function getNodeSql(ast) {
|
|
195
|
-
switch (ast[0]) {
|
|
196
|
-
case "$eq":
|
|
197
|
-
if (ast[2] === null)
|
|
198
|
-
return sql`${lookup2(ast[1])} IS NULL`;
|
|
199
|
-
return binop("=", ast[1], ast[2]);
|
|
200
|
-
case "$neq":
|
|
201
|
-
if (ast[2] === null)
|
|
202
|
-
return sql`${lookup2(ast[1])} IS NOT NULL`;
|
|
203
|
-
return binop("<>", ast[1], ast[2]);
|
|
204
|
-
case "$lt":
|
|
205
|
-
return binop("<", ast[1], ast[2]);
|
|
206
|
-
case "$lte":
|
|
207
|
-
return binop("<=", ast[1], ast[2]);
|
|
208
|
-
case "$gt":
|
|
209
|
-
return binop(">", ast[1], ast[2]);
|
|
210
|
-
case "$gte":
|
|
211
|
-
return binop(">=", ast[1], ast[2]);
|
|
212
|
-
case "$re":
|
|
213
|
-
return binop("~", ast[1], ast[2]);
|
|
214
|
-
case "$ire":
|
|
215
|
-
return binop("~*", ast[1], ast[2]);
|
|
216
|
-
case "$in":
|
|
217
|
-
return sql`${lookup2(ast[1])} IN (${join(ast[2])})`;
|
|
218
|
-
case "$nin":
|
|
219
|
-
return sql`${lookup2(ast[1])} NOT IN (${join(ast[2])})`;
|
|
220
|
-
case "$cts":
|
|
221
|
-
return sql`${lookup2(ast[1])} @> ${ast[2]}`;
|
|
222
|
-
case "$ctd":
|
|
223
|
-
return sql`${lookup2(ast[1])} <@ ${ast[2]}`;
|
|
224
|
-
case "$ovl":
|
|
225
|
-
switch (getColumnType(ast[1])) {
|
|
226
|
-
case "jsonb":
|
|
227
|
-
case "any":
|
|
228
|
-
return sql`${lookup2(ast[1])} ?| ${Array.isArray(ast[2]) ? ast[2] : Object.keys(ast[2])}`;
|
|
229
|
-
case "array":
|
|
230
|
-
return sql`${lookup2(ast[1])} && ${ast[2]}`;
|
|
231
|
-
default:
|
|
232
|
-
throw Error("pg.getSql_ovl_unknown_column_type");
|
|
233
|
-
}
|
|
234
|
-
case "$and":
|
|
235
|
-
return sql`(${join(ast[1].map((node) => getNodeSql(node)), `) AND (`)})`;
|
|
236
|
-
case "$or":
|
|
237
|
-
return sql`(${join(ast[1].map((node) => getNodeSql(node)), `) OR (`)})`;
|
|
238
|
-
case "$not":
|
|
239
|
-
return sql`NOT (${getNodeSql(ast[1])})`;
|
|
240
|
-
case "$any":
|
|
241
|
-
return sql`(SELECT bool_or(${getNodeSql(ast[3])}) FROM UNNEST(${lookup2(ast[1])}) ${lookup2(ast[2])})`;
|
|
242
|
-
case "$all":
|
|
243
|
-
return sql`(SELECT bool_and(${getNodeSql(ast[3])}) FROM UNNEST(${lookup2(ast[1])}) ${lookup2(ast[2])})`;
|
|
244
|
-
case "$has":
|
|
245
|
-
return sql`(SELECT bool_or(${join(ast[3].map((node) => getNodeSql(node)), `) AND bool_or(`)}) FROM UNNEST(${lookup2(ast[1])}) ${lookup2(ast[2])})`;
|
|
246
|
-
default:
|
|
247
|
-
throw Error("pg.getSql_unknown_operator: " + ast[0]);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return getNodeSql(getAst(filter));
|
|
251
|
-
}
|
|
252
153
|
const nowTimestamp = sql`cast(extract(epoch from now()) as integer)`;
|
|
253
154
|
const getJsonBuildObject = (variadic) => {
|
|
254
155
|
const args = join(Object.entries(variadic).map(([name, value]) => {
|
|
@@ -263,14 +164,9 @@ const getJsonBuildValue = (value) => {
|
|
|
263
164
|
return sql`${value}::text`;
|
|
264
165
|
return sql`${JSON.stringify(stripAttributes(value))}::jsonb`;
|
|
265
166
|
};
|
|
266
|
-
const lookup = (prop
|
|
167
|
+
const lookup = (prop) => {
|
|
267
168
|
const [prefix, ...suffix] = encodePath(prop);
|
|
268
|
-
|
|
269
|
-
return suffix.length ? sql`"${raw(prefix)}" ${op} ${suffix}` : sql`"${raw(prefix)}"`;
|
|
270
|
-
};
|
|
271
|
-
const getType = (prop) => {
|
|
272
|
-
const [_prefix, ...suffix] = encodePath(prop);
|
|
273
|
-
return suffix.length ? "jsonb" : "any";
|
|
169
|
+
return suffix.length ? sql`"${raw(prefix)}" #> ${suffix}` : sql`"${raw(prefix)}"`;
|
|
274
170
|
};
|
|
275
171
|
const aggSql = {
|
|
276
172
|
$sum: (prop) => sql`sum((${lookup(prop)})::numeric)`,
|
|
@@ -298,19 +194,39 @@ const getSelectCols = (table, projection = null) => {
|
|
|
298
194
|
}
|
|
299
195
|
return sql`jsonb_build_object(${join(sqls, ", ")})`;
|
|
300
196
|
};
|
|
197
|
+
function vertexSql(array) {
|
|
198
|
+
return sql`array[${join(array.map((num) => num === Infinity ? sql`'Infinity'` : num === -Infinity ? sql`'-Infinity'` : num))}]::float8[]`;
|
|
199
|
+
}
|
|
200
|
+
function castValue$1(value, type, name, isPut) {
|
|
201
|
+
if (!type)
|
|
202
|
+
throw Error("pg.write_no_column " + name);
|
|
203
|
+
if (value instanceof Sql)
|
|
204
|
+
return value;
|
|
205
|
+
if (value === null)
|
|
206
|
+
return sql`NULL`;
|
|
207
|
+
if (type === "jsonb") {
|
|
208
|
+
console.log("jsonb", value, type, name);
|
|
209
|
+
return isPut ? JSON.stringify(stripAttributes(value)) : sql`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})`;
|
|
210
|
+
}
|
|
211
|
+
if (type === "cube") {
|
|
212
|
+
if (!Array.isArray(value) || !value.length || Array.isArray(value[0]) && value.length !== 2) {
|
|
213
|
+
throw Error("pg.castValue_bad_cube" + JSON.stringify(value));
|
|
214
|
+
}
|
|
215
|
+
return Array.isArray(value[0]) ? sql`cube(${vertexSql(value[0])}, ${vertexSql(value[1])})` : sql`cube(${vertexSql(value)})`;
|
|
216
|
+
}
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
301
219
|
const getInsert = (row, options) => {
|
|
302
220
|
const cols = [];
|
|
303
221
|
const vals = [];
|
|
304
|
-
Object.entries(row).filter(([
|
|
222
|
+
Object.entries(row).filter(([col]) => col !== options.verCol && col[0] !== "$").concat([[options.verCol, nowTimestamp]]).forEach(([col, val]) => {
|
|
305
223
|
cols.push(sql`"${raw(col)}"`);
|
|
306
|
-
vals.push(val
|
|
224
|
+
vals.push(castValue$1(val, options.schema.types[col], col, row.$put));
|
|
307
225
|
});
|
|
308
226
|
return { cols: join(cols, ", "), vals: join(vals, ", ") };
|
|
309
227
|
};
|
|
310
228
|
const getUpdates = (row, options) => {
|
|
311
|
-
return join(Object.entries(row).filter(([
|
|
312
|
-
return sql`"${raw(name)}" = ${value instanceof Sql || typeof value !== "object" || !value ? value : !value.$put ? sql`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})` : sql`${JSON.stringify(stripAttributes(value))}::jsonb`}`;
|
|
313
|
-
}).concat(sql`"${raw(options.verCol)}" = ${nowTimestamp}`), ", ");
|
|
229
|
+
return join(Object.entries(row).filter(([col]) => col !== options.idCol && col[0] !== "$").map(([col, val]) => sql`"${raw(col)}" = ${castValue$1(val, options.schema.types[col], col, row.$put)}`).concat(sql`"${raw(options.verCol)}" = ${nowTimestamp}`), ", ");
|
|
314
230
|
};
|
|
315
231
|
function getJsonUpdate(_a, col, path) {
|
|
316
232
|
var _b = _a, { $put } = _b, object = __objRest(_b, ["$put"]);
|
|
@@ -337,6 +253,68 @@ function stripAttributes(object) {
|
|
|
337
253
|
return out;
|
|
338
254
|
}, {});
|
|
339
255
|
}
|
|
256
|
+
const opSql = {
|
|
257
|
+
$and: `AND`,
|
|
258
|
+
$or: `OR`,
|
|
259
|
+
$not: sql`NOT`,
|
|
260
|
+
$eq: sql`=`,
|
|
261
|
+
$neq: sql`<>`,
|
|
262
|
+
$in: sql`IN`,
|
|
263
|
+
$nin: sql`NOT IN`,
|
|
264
|
+
$lt: sql`<`,
|
|
265
|
+
$lte: sql`<=`,
|
|
266
|
+
$gt: sql`>`,
|
|
267
|
+
$gte: sql`>=`,
|
|
268
|
+
$re: sql`~`,
|
|
269
|
+
$ire: sql`~*`,
|
|
270
|
+
$cts: sql`@>`,
|
|
271
|
+
$ctd: sql`<@`
|
|
272
|
+
};
|
|
273
|
+
function castValue(value, type, op) {
|
|
274
|
+
if (value === null && op === "$eq")
|
|
275
|
+
return sql`IS NULL`;
|
|
276
|
+
if (value === null && op === "$neq")
|
|
277
|
+
return sql`IS NOT NULL`;
|
|
278
|
+
const sqlOp = opSql[op];
|
|
279
|
+
if (!sqlOp)
|
|
280
|
+
throw Error("pg.getSql_unknown_operator " + op);
|
|
281
|
+
if (op === "$in" || op === "$nin") {
|
|
282
|
+
return sql`${sqlOp} (${join(value)})`;
|
|
283
|
+
}
|
|
284
|
+
if (type === "jsonb") {
|
|
285
|
+
return sql`${sqlOp} ${JSON.stringify(value)}::jsonb`;
|
|
286
|
+
}
|
|
287
|
+
if (type === "cube") {
|
|
288
|
+
if (!Array.isArray(value) || !value.length || Array.isArray(value[0]) && value.length !== 2) {
|
|
289
|
+
throw Error("pg.castValue_bad_cube" + JSON.stringify(value));
|
|
290
|
+
}
|
|
291
|
+
return Array.isArray(value[0]) ? sql`${sqlOp} cube(${vertexSql(value[0])}, ${vertexSql(value[1])})` : sql`${sqlOp} cube(${vertexSql(value)})`;
|
|
292
|
+
}
|
|
293
|
+
return sql`${sqlOp} ${value}`;
|
|
294
|
+
}
|
|
295
|
+
function getSql(filter, options) {
|
|
296
|
+
function getNodeSql(ast) {
|
|
297
|
+
if (typeof ast === "boolean")
|
|
298
|
+
return ast;
|
|
299
|
+
const op = ast[0];
|
|
300
|
+
if (op === "$and" || op === "$or") {
|
|
301
|
+
return sql`(${join(ast[1].map((node) => getNodeSql(node)), `) ${opSql[op]} (`)})`;
|
|
302
|
+
} else if (op === "$not") {
|
|
303
|
+
return sql`${opSql[op]} (${getNodeSql(ast[1])})`;
|
|
304
|
+
}
|
|
305
|
+
const [prefix, ...suffix] = encodePath(ast[1]);
|
|
306
|
+
const { types } = options.schema;
|
|
307
|
+
if (!types[prefix])
|
|
308
|
+
throw Error("pg.no_column " + prefix);
|
|
309
|
+
if (suffix.length && types[prefix] !== "jsonb") {
|
|
310
|
+
throw Error("pg.lookup_not_jsonb " + prefix);
|
|
311
|
+
}
|
|
312
|
+
const [lhs, type] = suffix.length ? [sql`"${raw(prefix)}" #> ${suffix}`, "jsonb"] : [sql`"${raw(prefix)}"`, types[prefix]];
|
|
313
|
+
const rhs = castValue(ast[2], type, op);
|
|
314
|
+
return sql`${lhs} ${rhs}`;
|
|
315
|
+
}
|
|
316
|
+
return getNodeSql(getAst(filter));
|
|
317
|
+
}
|
|
340
318
|
const getIdMeta = ({ idCol }) => getJsonBuildObject({
|
|
341
319
|
$key: sql`"${raw(idCol)}"`,
|
|
342
320
|
$ver: nowTimestamp
|
|
@@ -358,13 +336,13 @@ function getArgSql(_c, options) {
|
|
|
358
336
|
throw Error("pg_arg.order_and_group_unsupported in " + prefix);
|
|
359
337
|
}
|
|
360
338
|
const meta = (key2) => $group ? getAggMeta(key2, $group) : getArgMeta(key2, prefix, idCol);
|
|
361
|
-
const groupCols = Array.isArray($group) && $group.length
|
|
339
|
+
const groupCols = Array.isArray($group) && $group.length && $group.map(lookup);
|
|
362
340
|
const group = groupCols ? join(groupCols, ", ") : void 0;
|
|
363
341
|
const hasRangeArg = $before || $after || $since || $until || $first || $last || $all || $order;
|
|
364
342
|
let key;
|
|
365
343
|
const where = [];
|
|
366
344
|
if (!isEmpty(filter)) {
|
|
367
|
-
where.push(getSql(filter,
|
|
345
|
+
where.push(getSql(filter, options));
|
|
368
346
|
key = sql`${JSON.stringify(filter)}::jsonb`;
|
|
369
347
|
}
|
|
370
348
|
if (!hasRangeArg)
|
|
@@ -525,24 +503,46 @@ class Db {
|
|
|
525
503
|
}
|
|
526
504
|
}
|
|
527
505
|
async readSql(sql2) {
|
|
528
|
-
|
|
529
|
-
result = result.rows.flat();
|
|
506
|
+
const result = (await this.query(sql2)).rows.flat();
|
|
530
507
|
log("Read result", result);
|
|
531
508
|
return result;
|
|
532
509
|
}
|
|
533
510
|
async writeSql(sql2) {
|
|
534
|
-
|
|
511
|
+
const res = await this.query(sql2);
|
|
535
512
|
log("Rows written", res.rowCount);
|
|
536
513
|
if (!res.rowCount) {
|
|
537
514
|
throw Error("pg.nothing_written " + sql2.text + " with " + sql2.values);
|
|
538
515
|
}
|
|
539
516
|
return res.rows[0][0];
|
|
540
517
|
}
|
|
518
|
+
async ensureSchema(tableOptions) {
|
|
519
|
+
if (tableOptions.schema)
|
|
520
|
+
return;
|
|
521
|
+
const { table } = tableOptions;
|
|
522
|
+
const types = (await this.query(sql`SELECT jsonb_object_agg(
|
|
523
|
+
column_name,
|
|
524
|
+
udt_name
|
|
525
|
+
)
|
|
526
|
+
FROM information_schema.columns
|
|
527
|
+
WHERE
|
|
528
|
+
table_name = ${table} AND
|
|
529
|
+
table_schema = (
|
|
530
|
+
SELECT table_schema
|
|
531
|
+
FROM information_schema.tables
|
|
532
|
+
WHERE table_name = ${table}
|
|
533
|
+
ORDER BY array_position(current_schemas(false)::text[], table_schema::text) ASC
|
|
534
|
+
LIMIT 1)`)).rows[0][0];
|
|
535
|
+
if (!types)
|
|
536
|
+
throw Error(`pg.missing_table ${table}`);
|
|
537
|
+
log("ensureSchema", types);
|
|
538
|
+
tableOptions.schema = { types };
|
|
539
|
+
}
|
|
541
540
|
async read(rootQuery, tableOptions) {
|
|
542
541
|
const idQueries = {};
|
|
543
542
|
const promises = [];
|
|
544
543
|
const results = [];
|
|
545
544
|
const { prefix } = tableOptions;
|
|
545
|
+
await this.ensureSchema(tableOptions);
|
|
546
546
|
const getByArgs = async (args, projection) => {
|
|
547
547
|
const result = await this.readSql(selectByArgs(args, projection, tableOptions));
|
|
548
548
|
const wrappedGraph = encodeGraph(wrapObject(result, prefix));
|
|
@@ -583,6 +583,7 @@ class Db {
|
|
|
583
583
|
}
|
|
584
584
|
async write(rootChange, tableOptions) {
|
|
585
585
|
const { prefix } = tableOptions;
|
|
586
|
+
await this.ensureSchema(tableOptions);
|
|
586
587
|
const change = unwrap(rootChange, prefix);
|
|
587
588
|
const sqls = change.map((node) => {
|
|
588
589
|
const arg = decodeArgs(node);
|
|
@@ -610,7 +611,7 @@ class Db {
|
|
|
610
611
|
return result;
|
|
611
612
|
}
|
|
612
613
|
}
|
|
613
|
-
const pg = ({ table, idCol, verCol, connection }) => (store) => {
|
|
614
|
+
const pg = ({ table, idCol, verCol, connection, schema }) => (store) => {
|
|
614
615
|
store.on("read", read);
|
|
615
616
|
store.on("write", write);
|
|
616
617
|
const prefix = store.path;
|
|
@@ -618,7 +619,8 @@ const pg = ({ table, idCol, verCol, connection }) => (store) => {
|
|
|
618
619
|
prefix,
|
|
619
620
|
table: table || prefix[prefix.length - 1] || "default",
|
|
620
621
|
idCol: idCol || "id",
|
|
621
|
-
verCol: verCol || "updatedAt"
|
|
622
|
+
verCol: verCol || "updatedAt",
|
|
623
|
+
schema
|
|
622
624
|
};
|
|
623
625
|
const defaultDb = new Db(connection);
|
|
624
626
|
function read(query, options, next) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graffy/pg",
|
|
3
3
|
"description": "The standard Postgres module for Graffy. Each instance this module mounts a Postgres table as a Graffy subtree.",
|
|
4
4
|
"author": "aravind (https://github.com/aravindet)",
|
|
5
|
-
"version": "0.15.
|
|
5
|
+
"version": "0.15.14-alpha.4",
|
|
6
6
|
"main": "./index.cjs",
|
|
7
7
|
"exports": {
|
|
8
8
|
"import": "./index.mjs",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@graffy/common": "0.15.
|
|
20
|
-
"
|
|
21
|
-
"
|
|
19
|
+
"@graffy/common": "0.15.14-alpha.4",
|
|
20
|
+
"sql-template-tag": "^4.0.0",
|
|
21
|
+
"debug": "^4.3.2"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"pg": "^8.0.0"
|
package/types/Db.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export default class Db {
|
|
|
4
4
|
query(sql: any): Promise<any>;
|
|
5
5
|
readSql(sql: any): Promise<any>;
|
|
6
6
|
writeSql(sql: any): Promise<any>;
|
|
7
|
+
ensureSchema(tableOptions: any): Promise<void>;
|
|
7
8
|
read(rootQuery: any, tableOptions: any): Promise<any>;
|
|
8
9
|
write(rootChange: any, tableOptions: any): Promise<any[]>;
|
|
9
10
|
}
|
package/types/filter/getSql.d.ts
CHANGED
package/types/index.d.ts
CHANGED
package/types/sql/clauses.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
export function vertexSql(array: any): Sql;
|
|
1
2
|
export const nowTimestamp: Sql;
|
|
2
3
|
export function getJsonBuildObject(variadic: any): Sql;
|
|
3
|
-
export function lookup(prop: any
|
|
4
|
-
export function getType(prop: any): "any" | "jsonb";
|
|
4
|
+
export function lookup(prop: any): Sql;
|
|
5
5
|
export function getSelectCols(table: any, projection?: any): Sql;
|
|
6
6
|
export function getInsert(row: any, options: any): {
|
|
7
7
|
cols: Sql;
|