@graffy/pg 0.16.3 → 0.16.4-alpha.2
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/Readme.md +6 -0
- package/index.cjs +99 -47
- package/index.mjs +99 -47
- package/package.json +2 -2
- package/types/Db.d.ts +1 -1
- package/types/index.d.ts +11 -6
- package/types/sql/getArgSql.d.ts +2 -1
package/Readme.md
CHANGED
|
@@ -20,6 +20,9 @@ store.use(path, pg(options));
|
|
|
20
20
|
- `table`, the name of the table. If not provided, the last segment of the `path` is used. This table must exist.
|
|
21
21
|
- `idCol`: the name of the column to use as ID. Defaults to `id`. This column must exist and be the primary key or have a unique constraint.
|
|
22
22
|
- `verCol`: the name of the column to store the Graffy version number. This column must exist, and must have a `DEFAULT` SQL expression defined - this expression is evaluated to calculate the version number. Graffy versions must monotonically increase, so this expression is typically based on `CURRENT_TIMESTAMP`.
|
|
23
|
+
- `joins`: other tables that have foreign keys referencing the ID column in this table, where we want to filter this table using columns of that other table via a "join". The value is a map of join names to join options.
|
|
24
|
+
- join names are the names used to refer to joined tables in filter expressions. Typically, these are the names of those tables.
|
|
25
|
+
- join options are objects with optional properties `table`, `idCol`, `refCol` and `verCol`. `refCol` is the name of the column in the join table that references this one.
|
|
23
26
|
- `connection`: a [pg](https://github.com/brianc/node-postgres) Client or Pool object (recommended), or the arguments for constructing a new Pool object. Optional.
|
|
24
27
|
|
|
25
28
|
### Database connection
|
|
@@ -89,6 +92,9 @@ Tags must contain both *foo* and *bar*. Note that the array of conditions here d
|
|
|
89
92
|
2. `{ tags: { $ctd: ['foo', 'bar', 'baz'] } }` becomes `tags <@ '{"foo","bar","baz"}'`.
|
|
90
93
|
Every tag must be one of *foo*, *bar* or *baz*.
|
|
91
94
|
|
|
95
|
+
#### Joins
|
|
96
|
+
1. `joinName: { ...expression }`,
|
|
97
|
+
|
|
92
98
|
#### Notes
|
|
93
99
|
|
|
94
100
|
1. We drop several MongoDB operators while retaining the capability:
|
package/index.cjs
CHANGED
|
@@ -126,9 +126,7 @@ function construct(node, prop, op) {
|
|
|
126
126
|
return construct(val, prop, key);
|
|
127
127
|
}
|
|
128
128
|
if (prop) {
|
|
129
|
-
|
|
130
|
-
return construct(val, prop + key);
|
|
131
|
-
throw Error(`pgast.unexpected_prop: ${key}`);
|
|
129
|
+
return ["$sub", prop, construct({ [key]: val })];
|
|
132
130
|
}
|
|
133
131
|
return construct(val, key);
|
|
134
132
|
})
|
|
@@ -140,6 +138,8 @@ function simplify(node) {
|
|
|
140
138
|
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
141
139
|
} else if (op === "$not") {
|
|
142
140
|
node[1] = simplify(node[1]);
|
|
141
|
+
} else if (op === "$sub") {
|
|
142
|
+
node[2] = simplify(node[2]);
|
|
143
143
|
}
|
|
144
144
|
if (op === "$and") {
|
|
145
145
|
if (!node[1].length)
|
|
@@ -385,36 +385,48 @@ function getBinarySql(lhs, type, op, value, textLhs) {
|
|
|
385
385
|
return sql`${lhs} ${sqlOp} ${cubeLiteralSql(value)}`;
|
|
386
386
|
return sql`${lhs} ${sqlOp} ${value}`;
|
|
387
387
|
}
|
|
388
|
-
function
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
388
|
+
function getNodeSql(ast, options) {
|
|
389
|
+
if (typeof ast === "boolean")
|
|
390
|
+
return ast;
|
|
391
|
+
const op = ast[0];
|
|
392
|
+
if (op === "$and" || op === "$or") {
|
|
393
|
+
return sql`(${join(
|
|
394
|
+
ast[1].map((node) => getNodeSql(node, options)),
|
|
395
|
+
`) ${opSql[op]} (`
|
|
396
|
+
)})`;
|
|
397
|
+
} else if (op === "$not") {
|
|
398
|
+
return sql`${opSql[op]} (${getNodeSql(ast[1], options)})`;
|
|
399
|
+
}
|
|
400
|
+
if (op === "$sub") {
|
|
401
|
+
const joinName = ast[1];
|
|
402
|
+
if (!options.joins[joinName])
|
|
403
|
+
throw Error(`pg.no_join ${joinName}`);
|
|
404
|
+
const { idCol, schema } = options;
|
|
405
|
+
const joinOptions = options.joins[joinName];
|
|
406
|
+
const { table: joinTable, refCol } = options.joins[joinName];
|
|
407
|
+
return sql`"${raw(idCol)}" IN (SELECT "${raw(refCol)}"::${raw(
|
|
408
|
+
schema.types[idCol]
|
|
409
|
+
)} FROM "${raw(joinTable)}" WHERE ${getNodeSql(ast[2], joinOptions)})`;
|
|
410
|
+
}
|
|
411
|
+
const [prefix, ...suffix] = ast[1].split(".");
|
|
412
|
+
const { types: types2 = {} } = options.schema;
|
|
413
|
+
if (!types2[prefix])
|
|
414
|
+
throw Error(`pg.no_column ${prefix}`);
|
|
415
|
+
if (types2[prefix] === "jsonb") {
|
|
416
|
+
const [lhs, textLhs] = suffix.length ? [
|
|
417
|
+
sql`"${raw(prefix)}" #> ${suffix}`,
|
|
418
|
+
sql`"${raw(prefix)}" #>> ${suffix}`
|
|
419
|
+
] : [sql`"${raw(prefix)}"`, sql`"${raw(prefix)}" #>> '{}'`];
|
|
420
|
+
return getBinarySql(lhs, "jsonb", op, ast[2], textLhs);
|
|
421
|
+
} else {
|
|
422
|
+
if (suffix.length)
|
|
423
|
+
throw Error(`pg.lookup_not_jsonb ${prefix}`);
|
|
424
|
+
return getBinarySql(sql`"${raw(prefix)}"`, types2[prefix], op, ast[2]);
|
|
416
425
|
}
|
|
417
|
-
|
|
426
|
+
}
|
|
427
|
+
function getSql(filter, options) {
|
|
428
|
+
const ast = getAst(filter);
|
|
429
|
+
return getNodeSql(ast, options);
|
|
418
430
|
}
|
|
419
431
|
const getIdMeta = ({ idCol, verDefault }) => sql`"${raw(idCol)}" AS "$key", ${raw(verDefault)} AS "$ver"`;
|
|
420
432
|
const getArgMeta = (key, { prefix, idCol, verDefault }) => sql`
|
|
@@ -442,7 +454,12 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
|
|
|
442
454
|
if (!common.isEmpty(filter))
|
|
443
455
|
where.push(getSql(filter, options));
|
|
444
456
|
if (!hasRangeArg)
|
|
445
|
-
return {
|
|
457
|
+
return {
|
|
458
|
+
meta: meta(baseKey),
|
|
459
|
+
where,
|
|
460
|
+
limit: 1,
|
|
461
|
+
ensureSingleRow: $group === true
|
|
462
|
+
};
|
|
446
463
|
const groupCols = Array.isArray($group) && $group.length && $group.map((prop) => lookup(prop, options));
|
|
447
464
|
const group = groupCols ? join(groupCols, ", ") : void 0;
|
|
448
465
|
const orderCols = ($order || [idCol]).map(
|
|
@@ -469,7 +486,8 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
|
|
|
469
486
|
where,
|
|
470
487
|
order,
|
|
471
488
|
group,
|
|
472
|
-
limit: $first || $last
|
|
489
|
+
limit: $first || $last,
|
|
490
|
+
ensureSingleRow: true
|
|
473
491
|
};
|
|
474
492
|
}
|
|
475
493
|
function getBoundCond(orderCols, bound, kind) {
|
|
@@ -503,9 +521,23 @@ function getBoundCond(orderCols, bound, kind) {
|
|
|
503
521
|
}
|
|
504
522
|
const MAX_LIMIT = 4096;
|
|
505
523
|
function selectByArgs(args, projection, options) {
|
|
506
|
-
const { table } = options;
|
|
507
|
-
const { where, order, group, limit, meta } = getArgSql(
|
|
524
|
+
const { table, idCol } = options;
|
|
525
|
+
const { where, order, group, limit, meta, ensureSingleRow } = getArgSql(
|
|
526
|
+
args,
|
|
527
|
+
options
|
|
528
|
+
);
|
|
508
529
|
const clampedLimit = Math.min(MAX_LIMIT, limit || MAX_LIMIT);
|
|
530
|
+
if (!ensureSingleRow) {
|
|
531
|
+
return sql`
|
|
532
|
+
SELECT
|
|
533
|
+
${getSelectCols(options, projection)}, ${meta}
|
|
534
|
+
FROM "${raw(table)}" WHERE "${raw(idCol)}" = (
|
|
535
|
+
SELECT "${raw(idCol)}" FROM "${raw(table)}"
|
|
536
|
+
${where.length ? sql`WHERE ${join(where, " AND ")}` : empty}
|
|
537
|
+
LIMIT 2
|
|
538
|
+
)
|
|
539
|
+
`;
|
|
540
|
+
}
|
|
509
541
|
return sql`
|
|
510
542
|
SELECT
|
|
511
543
|
${getSelectCols(options, projection)}, ${meta}
|
|
@@ -541,7 +573,7 @@ function getSingleSql(arg, options) {
|
|
|
541
573
|
SELECT "${raw(idCol)}"
|
|
542
574
|
FROM "${raw(table)}"
|
|
543
575
|
WHERE ${join(where, " AND ")}
|
|
544
|
-
LIMIT
|
|
576
|
+
LIMIT 2
|
|
545
577
|
)`,
|
|
546
578
|
meta
|
|
547
579
|
};
|
|
@@ -638,10 +670,10 @@ class Db {
|
|
|
638
670
|
}
|
|
639
671
|
return res.rows[0];
|
|
640
672
|
}
|
|
641
|
-
async ensureSchema(tableOptions) {
|
|
673
|
+
async ensureSchema(tableOptions, typeOids) {
|
|
642
674
|
if (tableOptions.schema)
|
|
643
675
|
return;
|
|
644
|
-
const { table, verCol } = tableOptions;
|
|
676
|
+
const { table, verCol, joins } = tableOptions;
|
|
645
677
|
const tableSchema = (await this.query(sql`
|
|
646
678
|
SELECT table_schema
|
|
647
679
|
FROM information_schema.tables
|
|
@@ -656,7 +688,7 @@ class Db {
|
|
|
656
688
|
table_schema = ${tableSchema}`)).rows[0].column_types;
|
|
657
689
|
if (!types2)
|
|
658
690
|
throw Error(`pg.missing_table ${table}`);
|
|
659
|
-
|
|
691
|
+
typeOids = typeOids || (await this.query(sql`
|
|
660
692
|
SELECT jsonb_object_agg(typname, oid) AS type_oids
|
|
661
693
|
FROM pg_type
|
|
662
694
|
WHERE typname = 'cube'`)).rows[0].type_oids;
|
|
@@ -670,6 +702,11 @@ class Db {
|
|
|
670
702
|
if (!verDefault) {
|
|
671
703
|
throw Error(`pg.verCol_without_default ${verCol}`);
|
|
672
704
|
}
|
|
705
|
+
await Promise.all(
|
|
706
|
+
Object.values(joins).map(
|
|
707
|
+
(joinOptions) => this.ensureSchema(joinOptions, typeOids)
|
|
708
|
+
)
|
|
709
|
+
);
|
|
673
710
|
log("ensureSchema", types2);
|
|
674
711
|
tableOptions.schema = { types: types2, typeOids };
|
|
675
712
|
tableOptions.verDefault = verDefault;
|
|
@@ -760,18 +797,33 @@ class Db {
|
|
|
760
797
|
return result;
|
|
761
798
|
}
|
|
762
799
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
const tableOpts = {
|
|
768
|
-
prefix,
|
|
769
|
-
table: table || prefix[prefix.length - 1] || "default",
|
|
800
|
+
function getTableOpts(name, { table, idCol, verCol, joins, schema, verDefault } = {}, parentName = null) {
|
|
801
|
+
const tableName = table || name;
|
|
802
|
+
return {
|
|
803
|
+
table: table || name,
|
|
770
804
|
idCol: idCol || "id",
|
|
771
805
|
verCol: verCol || "updatedAt",
|
|
806
|
+
joins: Object.fromEntries(
|
|
807
|
+
Object.entries(joins || {}).map(
|
|
808
|
+
([joinName, { refCol = parentName, ...joinOptions }]) => [
|
|
809
|
+
joinName,
|
|
810
|
+
{
|
|
811
|
+
refCol,
|
|
812
|
+
...getTableOpts(joinName, joinOptions, tableName)
|
|
813
|
+
}
|
|
814
|
+
]
|
|
815
|
+
)
|
|
816
|
+
),
|
|
772
817
|
schema,
|
|
773
818
|
verDefault
|
|
774
819
|
};
|
|
820
|
+
}
|
|
821
|
+
const pg = ({ connection, ...rawOptions }) => (store) => {
|
|
822
|
+
store.on("read", read);
|
|
823
|
+
store.on("write", write);
|
|
824
|
+
const prefix = store.path;
|
|
825
|
+
const tableOpts = getTableOpts(prefix[prefix.length - 1], rawOptions);
|
|
826
|
+
tableOpts.prefix = prefix;
|
|
775
827
|
const defaultDb = new Db(connection);
|
|
776
828
|
function read(query, options, next) {
|
|
777
829
|
const { pgClient } = options;
|
package/index.mjs
CHANGED
|
@@ -124,9 +124,7 @@ function construct(node, prop, op) {
|
|
|
124
124
|
return construct(val, prop, key);
|
|
125
125
|
}
|
|
126
126
|
if (prop) {
|
|
127
|
-
|
|
128
|
-
return construct(val, prop + key);
|
|
129
|
-
throw Error(`pgast.unexpected_prop: ${key}`);
|
|
127
|
+
return ["$sub", prop, construct({ [key]: val })];
|
|
130
128
|
}
|
|
131
129
|
return construct(val, key);
|
|
132
130
|
})
|
|
@@ -138,6 +136,8 @@ function simplify(node) {
|
|
|
138
136
|
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
139
137
|
} else if (op === "$not") {
|
|
140
138
|
node[1] = simplify(node[1]);
|
|
139
|
+
} else if (op === "$sub") {
|
|
140
|
+
node[2] = simplify(node[2]);
|
|
141
141
|
}
|
|
142
142
|
if (op === "$and") {
|
|
143
143
|
if (!node[1].length)
|
|
@@ -383,36 +383,48 @@ function getBinarySql(lhs, type, op, value, textLhs) {
|
|
|
383
383
|
return sql`${lhs} ${sqlOp} ${cubeLiteralSql(value)}`;
|
|
384
384
|
return sql`${lhs} ${sqlOp} ${value}`;
|
|
385
385
|
}
|
|
386
|
-
function
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
386
|
+
function getNodeSql(ast, options) {
|
|
387
|
+
if (typeof ast === "boolean")
|
|
388
|
+
return ast;
|
|
389
|
+
const op = ast[0];
|
|
390
|
+
if (op === "$and" || op === "$or") {
|
|
391
|
+
return sql`(${join(
|
|
392
|
+
ast[1].map((node) => getNodeSql(node, options)),
|
|
393
|
+
`) ${opSql[op]} (`
|
|
394
|
+
)})`;
|
|
395
|
+
} else if (op === "$not") {
|
|
396
|
+
return sql`${opSql[op]} (${getNodeSql(ast[1], options)})`;
|
|
397
|
+
}
|
|
398
|
+
if (op === "$sub") {
|
|
399
|
+
const joinName = ast[1];
|
|
400
|
+
if (!options.joins[joinName])
|
|
401
|
+
throw Error(`pg.no_join ${joinName}`);
|
|
402
|
+
const { idCol, schema } = options;
|
|
403
|
+
const joinOptions = options.joins[joinName];
|
|
404
|
+
const { table: joinTable, refCol } = options.joins[joinName];
|
|
405
|
+
return sql`"${raw(idCol)}" IN (SELECT "${raw(refCol)}"::${raw(
|
|
406
|
+
schema.types[idCol]
|
|
407
|
+
)} FROM "${raw(joinTable)}" WHERE ${getNodeSql(ast[2], joinOptions)})`;
|
|
408
|
+
}
|
|
409
|
+
const [prefix, ...suffix] = ast[1].split(".");
|
|
410
|
+
const { types: types2 = {} } = options.schema;
|
|
411
|
+
if (!types2[prefix])
|
|
412
|
+
throw Error(`pg.no_column ${prefix}`);
|
|
413
|
+
if (types2[prefix] === "jsonb") {
|
|
414
|
+
const [lhs, textLhs] = suffix.length ? [
|
|
415
|
+
sql`"${raw(prefix)}" #> ${suffix}`,
|
|
416
|
+
sql`"${raw(prefix)}" #>> ${suffix}`
|
|
417
|
+
] : [sql`"${raw(prefix)}"`, sql`"${raw(prefix)}" #>> '{}'`];
|
|
418
|
+
return getBinarySql(lhs, "jsonb", op, ast[2], textLhs);
|
|
419
|
+
} else {
|
|
420
|
+
if (suffix.length)
|
|
421
|
+
throw Error(`pg.lookup_not_jsonb ${prefix}`);
|
|
422
|
+
return getBinarySql(sql`"${raw(prefix)}"`, types2[prefix], op, ast[2]);
|
|
414
423
|
}
|
|
415
|
-
|
|
424
|
+
}
|
|
425
|
+
function getSql(filter, options) {
|
|
426
|
+
const ast = getAst(filter);
|
|
427
|
+
return getNodeSql(ast, options);
|
|
416
428
|
}
|
|
417
429
|
const getIdMeta = ({ idCol, verDefault }) => sql`"${raw(idCol)}" AS "$key", ${raw(verDefault)} AS "$ver"`;
|
|
418
430
|
const getArgMeta = (key, { prefix, idCol, verDefault }) => sql`
|
|
@@ -440,7 +452,12 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
|
|
|
440
452
|
if (!isEmpty(filter))
|
|
441
453
|
where.push(getSql(filter, options));
|
|
442
454
|
if (!hasRangeArg)
|
|
443
|
-
return {
|
|
455
|
+
return {
|
|
456
|
+
meta: meta(baseKey),
|
|
457
|
+
where,
|
|
458
|
+
limit: 1,
|
|
459
|
+
ensureSingleRow: $group === true
|
|
460
|
+
};
|
|
444
461
|
const groupCols = Array.isArray($group) && $group.length && $group.map((prop) => lookup(prop, options));
|
|
445
462
|
const group = groupCols ? join(groupCols, ", ") : void 0;
|
|
446
463
|
const orderCols = ($order || [idCol]).map(
|
|
@@ -467,7 +484,8 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
|
|
|
467
484
|
where,
|
|
468
485
|
order,
|
|
469
486
|
group,
|
|
470
|
-
limit: $first || $last
|
|
487
|
+
limit: $first || $last,
|
|
488
|
+
ensureSingleRow: true
|
|
471
489
|
};
|
|
472
490
|
}
|
|
473
491
|
function getBoundCond(orderCols, bound, kind) {
|
|
@@ -501,9 +519,23 @@ function getBoundCond(orderCols, bound, kind) {
|
|
|
501
519
|
}
|
|
502
520
|
const MAX_LIMIT = 4096;
|
|
503
521
|
function selectByArgs(args, projection, options) {
|
|
504
|
-
const { table } = options;
|
|
505
|
-
const { where, order, group, limit, meta } = getArgSql(
|
|
522
|
+
const { table, idCol } = options;
|
|
523
|
+
const { where, order, group, limit, meta, ensureSingleRow } = getArgSql(
|
|
524
|
+
args,
|
|
525
|
+
options
|
|
526
|
+
);
|
|
506
527
|
const clampedLimit = Math.min(MAX_LIMIT, limit || MAX_LIMIT);
|
|
528
|
+
if (!ensureSingleRow) {
|
|
529
|
+
return sql`
|
|
530
|
+
SELECT
|
|
531
|
+
${getSelectCols(options, projection)}, ${meta}
|
|
532
|
+
FROM "${raw(table)}" WHERE "${raw(idCol)}" = (
|
|
533
|
+
SELECT "${raw(idCol)}" FROM "${raw(table)}"
|
|
534
|
+
${where.length ? sql`WHERE ${join(where, " AND ")}` : empty}
|
|
535
|
+
LIMIT 2
|
|
536
|
+
)
|
|
537
|
+
`;
|
|
538
|
+
}
|
|
507
539
|
return sql`
|
|
508
540
|
SELECT
|
|
509
541
|
${getSelectCols(options, projection)}, ${meta}
|
|
@@ -539,7 +571,7 @@ function getSingleSql(arg, options) {
|
|
|
539
571
|
SELECT "${raw(idCol)}"
|
|
540
572
|
FROM "${raw(table)}"
|
|
541
573
|
WHERE ${join(where, " AND ")}
|
|
542
|
-
LIMIT
|
|
574
|
+
LIMIT 2
|
|
543
575
|
)`,
|
|
544
576
|
meta
|
|
545
577
|
};
|
|
@@ -636,10 +668,10 @@ class Db {
|
|
|
636
668
|
}
|
|
637
669
|
return res.rows[0];
|
|
638
670
|
}
|
|
639
|
-
async ensureSchema(tableOptions) {
|
|
671
|
+
async ensureSchema(tableOptions, typeOids) {
|
|
640
672
|
if (tableOptions.schema)
|
|
641
673
|
return;
|
|
642
|
-
const { table, verCol } = tableOptions;
|
|
674
|
+
const { table, verCol, joins } = tableOptions;
|
|
643
675
|
const tableSchema = (await this.query(sql`
|
|
644
676
|
SELECT table_schema
|
|
645
677
|
FROM information_schema.tables
|
|
@@ -654,7 +686,7 @@ class Db {
|
|
|
654
686
|
table_schema = ${tableSchema}`)).rows[0].column_types;
|
|
655
687
|
if (!types2)
|
|
656
688
|
throw Error(`pg.missing_table ${table}`);
|
|
657
|
-
|
|
689
|
+
typeOids = typeOids || (await this.query(sql`
|
|
658
690
|
SELECT jsonb_object_agg(typname, oid) AS type_oids
|
|
659
691
|
FROM pg_type
|
|
660
692
|
WHERE typname = 'cube'`)).rows[0].type_oids;
|
|
@@ -668,6 +700,11 @@ class Db {
|
|
|
668
700
|
if (!verDefault) {
|
|
669
701
|
throw Error(`pg.verCol_without_default ${verCol}`);
|
|
670
702
|
}
|
|
703
|
+
await Promise.all(
|
|
704
|
+
Object.values(joins).map(
|
|
705
|
+
(joinOptions) => this.ensureSchema(joinOptions, typeOids)
|
|
706
|
+
)
|
|
707
|
+
);
|
|
671
708
|
log("ensureSchema", types2);
|
|
672
709
|
tableOptions.schema = { types: types2, typeOids };
|
|
673
710
|
tableOptions.verDefault = verDefault;
|
|
@@ -758,18 +795,33 @@ class Db {
|
|
|
758
795
|
return result;
|
|
759
796
|
}
|
|
760
797
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const tableOpts = {
|
|
766
|
-
prefix,
|
|
767
|
-
table: table || prefix[prefix.length - 1] || "default",
|
|
798
|
+
function getTableOpts(name, { table, idCol, verCol, joins, schema, verDefault } = {}, parentName = null) {
|
|
799
|
+
const tableName = table || name;
|
|
800
|
+
return {
|
|
801
|
+
table: table || name,
|
|
768
802
|
idCol: idCol || "id",
|
|
769
803
|
verCol: verCol || "updatedAt",
|
|
804
|
+
joins: Object.fromEntries(
|
|
805
|
+
Object.entries(joins || {}).map(
|
|
806
|
+
([joinName, { refCol = parentName, ...joinOptions }]) => [
|
|
807
|
+
joinName,
|
|
808
|
+
{
|
|
809
|
+
refCol,
|
|
810
|
+
...getTableOpts(joinName, joinOptions, tableName)
|
|
811
|
+
}
|
|
812
|
+
]
|
|
813
|
+
)
|
|
814
|
+
),
|
|
770
815
|
schema,
|
|
771
816
|
verDefault
|
|
772
817
|
};
|
|
818
|
+
}
|
|
819
|
+
const pg = ({ connection, ...rawOptions }) => (store) => {
|
|
820
|
+
store.on("read", read);
|
|
821
|
+
store.on("write", write);
|
|
822
|
+
const prefix = store.path;
|
|
823
|
+
const tableOpts = getTableOpts(prefix[prefix.length - 1], rawOptions);
|
|
824
|
+
tableOpts.prefix = prefix;
|
|
773
825
|
const defaultDb = new Db(connection);
|
|
774
826
|
function read(query, options, next) {
|
|
775
827
|
const { pgClient } = options;
|
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.16.
|
|
5
|
+
"version": "0.16.4-alpha.2",
|
|
6
6
|
"main": "./index.cjs",
|
|
7
7
|
"exports": {
|
|
8
8
|
"import": "./index.mjs",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@graffy/common": "0.16.
|
|
19
|
+
"@graffy/common": "0.16.4-alpha.2",
|
|
20
20
|
"debug": "^4.3.3"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
package/types/Db.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export default class Db {
|
|
|
4
4
|
query(sql: any, tableOptions: any): Promise<any>;
|
|
5
5
|
readSql(sql: any, tableOptions: any): Promise<any>;
|
|
6
6
|
writeSql(sql: any, tableOptions: any): Promise<any>;
|
|
7
|
-
ensureSchema(tableOptions: any): Promise<void>;
|
|
7
|
+
ensureSchema(tableOptions: any, typeOids: any): Promise<void>;
|
|
8
8
|
read(rootQuery: any, tableOptions: any): Promise<any>;
|
|
9
9
|
write(rootChange: any, tableOptions: any): Promise<any[]>;
|
|
10
10
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
export function pg({
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export function pg({ connection, ...rawOptions }: Partial<TableOpts> & {
|
|
2
|
+
connection: any;
|
|
3
|
+
}): Function;
|
|
4
|
+
export type TableOpts = {
|
|
5
|
+
table: string;
|
|
6
|
+
idCol: string;
|
|
7
|
+
verCol: string;
|
|
8
|
+
joins: Record<string, Partial<TableOpts> & {
|
|
9
|
+
refCol: string;
|
|
10
|
+
}>;
|
|
6
11
|
schema?: any;
|
|
7
12
|
verDefault?: string;
|
|
8
|
-
}
|
|
13
|
+
};
|
package/types/sql/getArgSql.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
@param {{prefix: string, idCol: string, verDefault: string}} options
|
|
6
6
|
|
|
7
7
|
@typedef { import('sql-template-tag').Sql } Sql
|
|
8
|
-
@return {{ meta: Sql, where: Sql[], order?: Sql, group?: Sql, limit: number }}
|
|
8
|
+
@return {{ meta: Sql, where: Sql[], order?: Sql, group?: Sql, limit: number, ensureSingleRow: boolean }}
|
|
9
9
|
*/
|
|
10
10
|
export default function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $cursor: _, ...rest }: object, options: {
|
|
11
11
|
prefix: string;
|
|
@@ -17,6 +17,7 @@ export default function getArgSql({ $first, $last, $after, $before, $since, $unt
|
|
|
17
17
|
order?: Sql;
|
|
18
18
|
group?: Sql;
|
|
19
19
|
limit: number;
|
|
20
|
+
ensureSingleRow: boolean;
|
|
20
21
|
};
|
|
21
22
|
/**
|
|
22
23
|
* Uses the args object (typically passed in the $key attribute)
|