@graffy/pg 0.19.0 → 0.19.1-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.
@@ -1,6 +1,5 @@
1
1
  export default class Db {
2
2
  constructor(connection: any);
3
- client: any;
4
3
  query(sql: any, tableOptions: any): Promise<any>;
5
4
  readSql(sql: any, tableOptions: any): Promise<any>;
6
5
  writeSql(sql: any, tableOptions: any): Promise<any>;
package/Db.js ADDED
@@ -0,0 +1,236 @@
1
+ import { cmp, decodeArgs, decodeGraph, decodeQuery, encodeGraph, encodePath, finalize, isEmpty, isPlainObject, isRange, merge, mergeObject, unwrap, wrap, wrapObject, } from '@graffy/common';
2
+ import debug from 'debug';
3
+ import pg from 'pg';
4
+ import sqlTag from 'sql-template-tag';
5
+ import formatSql from "./sql/format.js";
6
+ import { del, patch, put, selectByArgs, selectByIds } from "./sql/index.js";
7
+ const log = debug('graffy:pg:db');
8
+ const { Pool, Client, types } = pg;
9
+ export default class Db {
10
+ constructor(connection) {
11
+ if (typeof connection === 'object' &&
12
+ connection &&
13
+ (connection instanceof Pool || connection instanceof Client)) {
14
+ this.client = connection;
15
+ }
16
+ else {
17
+ this.client = new Pool(connection);
18
+ }
19
+ }
20
+ async query(sql, tableOptions) {
21
+ log(`Making SQL query: ${sql.text}`, sql.values);
22
+ const cubeOid = Number.parseInt(tableOptions?.schema?.typeOids?.cube || '0', 10) || null;
23
+ try {
24
+ sql.types = {
25
+ getTypeParser: (oid, format) => {
26
+ if (oid === types.builtins.INT8) {
27
+ return (value) => Number.parseInt(value, 10);
28
+ }
29
+ if (oid === cubeOid) {
30
+ return (value) => {
31
+ const array = value
32
+ .slice(1, -1)
33
+ .split(/\)\s*,\s*\(/)
34
+ .map((corner) => corner
35
+ .split(',')
36
+ .map((coord) => Number.parseFloat(coord.trim())));
37
+ return array.length > 1 ? array : array[0];
38
+ };
39
+ }
40
+ return types.getTypeParser(oid, format);
41
+ },
42
+ };
43
+ return await this.client.query(sql);
44
+ }
45
+ catch (e) {
46
+ const message = [
47
+ e.message,
48
+ e.detail,
49
+ e.hint,
50
+ e.where,
51
+ sql.text,
52
+ JSON.stringify(sql.values),
53
+ ]
54
+ .filter(Boolean)
55
+ .join('; ');
56
+ throw Error(`pg.sql_error ${message}`);
57
+ }
58
+ }
59
+ async readSql(sql, tableOptions) {
60
+ const result = (await this.query(sql, tableOptions)).rows;
61
+ // Each row is an array, as there is only one column returned.
62
+ log('Read result', result);
63
+ return result;
64
+ }
65
+ async writeSql(sql, tableOptions) {
66
+ const res = await this.query(sql, tableOptions);
67
+ log('Rows written', res.rowCount);
68
+ if (!res.rowCount) {
69
+ throw Error(`pg.nothing_written ${sql.text} with ${sql.values}`);
70
+ }
71
+ return res.rows;
72
+ }
73
+ /*
74
+ Adds .schema to tableOptions if it doesn't exist yet.
75
+ It mutates the argument, to "persist" the results and
76
+ avoid this query in every operation.
77
+ */
78
+ async ensureSchema(tableOptions, typeOids) {
79
+ if (tableOptions.schema)
80
+ return;
81
+ const { table, verCol, joins } = tableOptions;
82
+ const tableInfoSchema = (await this.query(sqlTag `
83
+ SELECT table_schema, table_type
84
+ FROM information_schema.tables
85
+ WHERE table_name = ${table}
86
+ ORDER BY array_position(current_schemas(false)::text[], table_schema::text) ASC
87
+ LIMIT 1`)).rows[0];
88
+ const tableSchema = tableInfoSchema.table_schema;
89
+ const tableType = tableInfoSchema.table_type;
90
+ const types = (await this.query(sqlTag `
91
+ SELECT jsonb_object_agg(column_name, udt_name) AS column_types
92
+ FROM information_schema.columns
93
+ WHERE
94
+ table_name = ${table} AND
95
+ table_schema = ${tableSchema}`)).rows[0].column_types;
96
+ if (!types)
97
+ throw Error(`pg.missing_table ${table}`);
98
+ typeOids =
99
+ typeOids ||
100
+ (await this.query(sqlTag `
101
+ SELECT jsonb_object_agg(typname, oid) AS type_oids
102
+ FROM pg_type
103
+ WHERE typname = 'cube'`)).rows[0].type_oids;
104
+ // console.log({ typeOids });
105
+ const verDefault = (await this.query(sqlTag `
106
+ SELECT column_default
107
+ FROM information_schema.columns
108
+ WHERE
109
+ table_name = ${table} AND
110
+ table_schema = ${tableSchema} AND
111
+ column_name = ${verCol}`)).rows[0].column_default;
112
+ if (!verDefault && tableType !== 'VIEW') {
113
+ throw Error(`pg.verCol_without_default ${verCol}`);
114
+ }
115
+ await Promise.all(Object.values(joins).map((joinOptions) => this.ensureSchema(joinOptions, typeOids)));
116
+ log('ensureSchema', types);
117
+ tableOptions.schema = { types, typeOids };
118
+ tableOptions.verDefault = verDefault;
119
+ }
120
+ async read(rootQuery, tableOptions) {
121
+ const idQueries = {};
122
+ const promises = [];
123
+ const results = [];
124
+ const { prefix: rawPrefix } = tableOptions;
125
+ const prefix = encodePath(rawPrefix);
126
+ await this.ensureSchema(tableOptions);
127
+ const getByArgs = async (args, projection) => {
128
+ const sql = selectByArgs(args, projection, tableOptions);
129
+ const result = await this.readSql(sql, tableOptions);
130
+ const wrappedGraph = encodeGraph(wrapObject(result, rawPrefix));
131
+ log('getByArgs', wrappedGraph);
132
+ merge(results, wrappedGraph);
133
+ };
134
+ const explainArgs = async (args, _projection) => {
135
+ const { analyze, $explain: qArgs } = args;
136
+ const qSql = selectByArgs(qArgs, null, tableOptions);
137
+ const sql = sqlTag `EXPLAIN (${analyze ? sqlTag `ANALYZE, BUFFERS, TIMING, ` : sqlTag ``}COSTS, VERBOSE, FORMAT JSON) ${qSql}`;
138
+ const result = await this.readSql(sql, tableOptions);
139
+ const wrappedGraph = encodeGraph(wrapObject({
140
+ $key: args,
141
+ sql: formatSql(qSql),
142
+ plan: result[0]['QUERY PLAN'][0],
143
+ }, rawPrefix));
144
+ log('explainArgs', wrappedGraph);
145
+ merge(results, wrappedGraph);
146
+ };
147
+ const getByIds = async () => {
148
+ // TODO: Calculate a combined projection.
149
+ // Bonus: Strategically split into multiple read operations
150
+ // based on projection.
151
+ const sql = selectByIds(Object.keys(idQueries), null, tableOptions);
152
+ const result = await this.readSql(sql, tableOptions);
153
+ for (const object of result) {
154
+ const wrappedGraph = encodeGraph(wrapObject(object, rawPrefix));
155
+ log('getByIds', wrappedGraph);
156
+ merge(results, wrappedGraph);
157
+ }
158
+ };
159
+ const query = unwrap(rootQuery, prefix);
160
+ for (const node of query) {
161
+ const args = decodeArgs(node);
162
+ if (isPlainObject(args)) {
163
+ if (node.prefix) {
164
+ for (const childNode of node.children) {
165
+ const childArgs = decodeArgs(childNode);
166
+ const projection = childNode.children
167
+ ? decodeQuery(childNode.children)
168
+ : null;
169
+ promises.push(getByArgs({ ...args, ...childArgs }, projection));
170
+ }
171
+ }
172
+ else {
173
+ const projection = node.children ? decodeQuery(node.children) : null;
174
+ if (args.$explain) {
175
+ promises.push(explainArgs(args, projection));
176
+ }
177
+ else {
178
+ promises.push(getByArgs(args, projection));
179
+ }
180
+ }
181
+ }
182
+ else {
183
+ idQueries[args] = node.children;
184
+ }
185
+ }
186
+ if (!isEmpty(idQueries))
187
+ promises.push(getByIds());
188
+ await Promise.all(promises);
189
+ log('dbRead', rootQuery, results);
190
+ return finalize(results, wrap(query, prefix));
191
+ }
192
+ async write(rootChange, tableOptions) {
193
+ const { prefix: rawPrefix } = tableOptions;
194
+ const prefix = encodePath(rawPrefix);
195
+ await this.ensureSchema(tableOptions);
196
+ const change = unwrap(rootChange, prefix);
197
+ const puts = [];
198
+ const sqls = [];
199
+ for (const node of change) {
200
+ const arg = decodeArgs(node);
201
+ if (isRange(node)) {
202
+ if (cmp(node.key, node.end) === 0) {
203
+ log('delete', node);
204
+ sqls.push(del(arg, tableOptions));
205
+ continue;
206
+ }
207
+ throw Error('pg_write.write_range_unsupported');
208
+ }
209
+ const object = decodeGraph(node.children);
210
+ if (isPlainObject(arg)) {
211
+ mergeObject(object, arg);
212
+ }
213
+ else {
214
+ object[tableOptions.idCol] = arg;
215
+ }
216
+ if (object.$put && object.$put !== true) {
217
+ throw Error('pg_write.partial_put_unsupported');
218
+ }
219
+ if (object.$put) {
220
+ puts.push([object, arg]);
221
+ }
222
+ else {
223
+ sqls.push(patch(object, arg, tableOptions));
224
+ }
225
+ }
226
+ if (puts.length)
227
+ sqls.push(...put(puts, tableOptions));
228
+ const result = [];
229
+ await Promise.all(sqls.map((sql) => this.writeSql(sql, tableOptions).then((object) => {
230
+ log('returned_object_wrapped', wrapObject(object, rawPrefix));
231
+ merge(result, encodeGraph(wrapObject(object, rawPrefix)));
232
+ })));
233
+ log('dbWrite', rootChange, result);
234
+ return result;
235
+ }
236
+ }
@@ -0,0 +1,39 @@
1
+ import { encodePath, unwrapObject } from '@graffy/common';
2
+ import getAst from "./getAst.js";
3
+ export default function filterObject(filter, object) {
4
+ function lookup(path) {
5
+ return unwrapObject(object, encodePath(path));
6
+ }
7
+ function checkNode(ast) {
8
+ switch (ast[0]) {
9
+ case '$eq':
10
+ return lookup(ast[1]) === ast[2];
11
+ case '$ne':
12
+ return lookup(ast[1]) !== ast[2];
13
+ case '$lt':
14
+ return lookup(ast[1]) < ast[2];
15
+ case '$lte':
16
+ return lookup(ast[1]) <= ast[2];
17
+ case '$gt':
18
+ return lookup(ast[1]) > ast[2];
19
+ case '$gte':
20
+ return lookup(ast[1]) >= ast[2];
21
+ case '$in':
22
+ return Array.isArray(ast[2]) && ast[2].includes(lookup(ast[1]));
23
+ case '$nin':
24
+ return !(Array.isArray(ast[2]) && ast[2].includes(lookup(ast[1])));
25
+ case '$cts':
26
+ case '$ctd':
27
+ case '$ovp':
28
+ case '$and':
29
+ case '$or':
30
+ case '$not':
31
+ case '$any':
32
+ case '$all':
33
+ case '$has':
34
+ throw Error('pgfilter.unimplemented');
35
+ }
36
+ }
37
+ const rootAst = getAst(filter);
38
+ return checkNode(rootAst);
39
+ }
@@ -0,0 +1,152 @@
1
+ const valid = {
2
+ $eq: true,
3
+ $lt: true,
4
+ $gt: true,
5
+ $lte: true,
6
+ $gte: true,
7
+ $re: true,
8
+ $ire: true,
9
+ $text: true,
10
+ $and: true,
11
+ $or: true,
12
+ $any: true,
13
+ $all: true,
14
+ $has: true,
15
+ $cts: true,
16
+ $ctd: true,
17
+ $keycts: true,
18
+ $keyctd: true,
19
+ };
20
+ const inverse = {
21
+ $eq: '$neq',
22
+ $neq: '$eq',
23
+ $in: '$nin',
24
+ $nin: '$in',
25
+ $lt: '$gte',
26
+ $gte: '$lt',
27
+ $gt: '$lte',
28
+ $lte: '$gt',
29
+ };
30
+ export default function getAst(filter) {
31
+ return simplify(construct(filter));
32
+ }
33
+ function isValidSubQuery(node) {
34
+ if (!node || typeof node !== 'object')
35
+ return false;
36
+ const keys = Object.keys(node);
37
+ for (const key of keys) {
38
+ if (key[0] === '$' && !['$and', '$or', '$not'].includes(key))
39
+ return false;
40
+ if (key[0] !== '$')
41
+ return true;
42
+ }
43
+ for (const key in node) {
44
+ if (!isValidSubQuery(node[key]))
45
+ return false;
46
+ }
47
+ return false;
48
+ }
49
+ function construct(node, prop, op) {
50
+ if (!node || typeof node !== 'object' || (prop && op)) {
51
+ if (op && prop)
52
+ return [op, prop, node];
53
+ if (prop)
54
+ return ['$eq', prop, node];
55
+ throw Error(`pgast.expected_prop_before:${JSON.stringify(node)}`);
56
+ }
57
+ if (Array.isArray(node)) {
58
+ return ['$or', node.map((item) => construct(item, prop, op))];
59
+ }
60
+ if (prop && isValidSubQuery(node)) {
61
+ return ['$sub', prop, construct(node)];
62
+ }
63
+ return [
64
+ '$and',
65
+ Object.entries(node).map(([key, val]) => {
66
+ if (key === '$or' || key === '$and') {
67
+ return [key, construct(val, prop, op)[1]];
68
+ }
69
+ if (key === '$not') {
70
+ return [key, construct(val, prop, op)];
71
+ }
72
+ if (key[0] === '$') {
73
+ if (!valid[key])
74
+ throw Error(`pgast.invalid_op:${key}`);
75
+ if (op)
76
+ throw Error(`pgast.unexpected_op:${op} before:${key}`);
77
+ if (!prop)
78
+ throw Error(`pgast.expected_prop_before:${key}`);
79
+ return construct(val, prop, key);
80
+ }
81
+ return construct(val, key);
82
+ }),
83
+ ];
84
+ }
85
+ function simplify(node) {
86
+ const op = node[0];
87
+ // TODO: $and/$or with multiple $subs with same prop ->
88
+ // single $sub with $and/$or inside
89
+ // Recurse into subnodes and simplify them first.
90
+ if (op === '$and' || op === '$or') {
91
+ node[1] = node[1].map((subnode) => simplify(subnode));
92
+ }
93
+ else if (op === '$not') {
94
+ node[1] = simplify(node[1]);
95
+ }
96
+ else if (op === '$sub') {
97
+ node[2] = simplify(node[2]);
98
+ }
99
+ // Handle empty $and/$or and booleans
100
+ if (op === '$and') {
101
+ if (!node[1].length)
102
+ return true;
103
+ if (node[1].includes(false))
104
+ return false;
105
+ node[1] = node[1].filter((item) => item !== true);
106
+ }
107
+ else if (op === '$or') {
108
+ if (!node[1].length)
109
+ return false;
110
+ if (node[1].includes(true))
111
+ return true;
112
+ node[1] = node[1].filter((item) => item !== false);
113
+ }
114
+ else if (op === '$not' && typeof node[1] === 'boolean') {
115
+ return !node[1];
116
+ }
117
+ // $or with multiple $eq limbs with same prop -> $in
118
+ if (op === '$or') {
119
+ const { eqmap, noneq, change } = node[1].reduce((acc, item) => {
120
+ if (item[0] !== '$eq' || item[2] === null) {
121
+ acc.noneq.push(item);
122
+ }
123
+ else if (acc.eqmap[item[1]]) {
124
+ acc.change = true;
125
+ acc.eqmap[item[1]].push(item[2]);
126
+ return acc;
127
+ }
128
+ else {
129
+ acc.eqmap[item[1]] = [item[2]];
130
+ }
131
+ return acc;
132
+ }, { eqmap: {}, noneq: [], change: false });
133
+ // Don't return. Modify node and then apply the next rule.
134
+ if (change) {
135
+ node[1] = [
136
+ ...noneq,
137
+ ...Object.entries(eqmap).map(([prop, val]) => val.length > 1 ? ['$in', prop, val] : ['$eq', prop, val[0]]),
138
+ ];
139
+ }
140
+ }
141
+ // unwrap $and / $or with only one limb
142
+ if ((op === '$and' || op === '$or') && node[1].length === 1) {
143
+ return node[1][0];
144
+ }
145
+ // $not $eq -> $neq, $in -> $nin etc.
146
+ if (op === '$not') {
147
+ const [subop, ...subargs] = node[1];
148
+ const invop = inverse[subop];
149
+ return invop ? [invop, ...subargs] : node;
150
+ }
151
+ return node;
152
+ }
@@ -0,0 +1,100 @@
1
+ import sql, { join, raw } from 'sql-template-tag';
2
+ import { cubeLiteralSql } from "../sql/clauses.js";
3
+ import getAst from "./getAst.js";
4
+ const opSql = {
5
+ $and: 'AND', // Not SQL as these are used as delimiters
6
+ $or: 'OR',
7
+ $not: sql `NOT`,
8
+ $eq: sql `=`,
9
+ $neq: sql `<>`,
10
+ $in: sql `IN`,
11
+ $nin: sql `NOT IN`,
12
+ $lt: sql `<`,
13
+ $lte: sql `<=`,
14
+ $gt: sql `>`,
15
+ $gte: sql `>=`,
16
+ $re: sql `~`,
17
+ $ire: sql `~*`,
18
+ $cts: sql `@>`,
19
+ $ctd: sql `<@`,
20
+ $keycts: sql `?|`,
21
+ $keyctd: sql `?&`,
22
+ };
23
+ function getBinarySql(lhs, type, op, value, textLhs) {
24
+ if (value === null && op === '$eq')
25
+ return sql `${lhs} IS NULL`;
26
+ if (value === null && op === '$neq')
27
+ return sql `${lhs} IS NOT NULL`;
28
+ const sqlOp = opSql[op];
29
+ if (!sqlOp)
30
+ throw Error(`pg.getSql_unknown_operator ${op}`);
31
+ if (op === '$in' || op === '$nin') {
32
+ if (type === 'jsonb' && typeof value[0] === 'string')
33
+ lhs = textLhs;
34
+ return sql `${lhs} ${sqlOp} (${join(value)})`;
35
+ }
36
+ if (op === '$re' || op === '$ire') {
37
+ if (type === 'jsonb') {
38
+ lhs = textLhs;
39
+ }
40
+ else if (type !== 'text') {
41
+ lhs = sql `(${lhs})::text`;
42
+ }
43
+ return sql `${lhs} ${sqlOp} ${String(value)}`;
44
+ }
45
+ if (type === 'jsonb') {
46
+ if (typeof value === 'string') {
47
+ return sql `${textLhs} ${sqlOp} ${value}`;
48
+ }
49
+ if ((op === '$keycts' || op === '$keyctd') && Array.isArray(value))
50
+ return sql `${lhs} ${sqlOp} ${value}::text[]`;
51
+ return sql `${lhs} ${sqlOp} ${JSON.stringify(value)}::jsonb`;
52
+ }
53
+ if (type === 'cube')
54
+ return sql `${lhs} ${sqlOp} ${cubeLiteralSql(value)}`;
55
+ return sql `${lhs} ${sqlOp} ${value}`;
56
+ }
57
+ function getNodeSql(ast, options) {
58
+ if (typeof ast === 'boolean')
59
+ return ast;
60
+ const op = ast[0];
61
+ if (op === '$and' || op === '$or') {
62
+ // Handle variadic operators
63
+ return sql `(${join(ast[1].map((node) => getNodeSql(node, options)), `) ${opSql[op]} (`)})`;
64
+ }
65
+ if (op === '$not') {
66
+ // Handle unary operators
67
+ return sql `${opSql[op]} (${getNodeSql(ast[1], options)})`;
68
+ }
69
+ if (op === '$sub') {
70
+ // Handle joins.
71
+ const joinName = ast[1];
72
+ if (!options.joins[joinName])
73
+ throw Error(`pg.no_join ${joinName}`);
74
+ const { idCol, schema } = options;
75
+ const joinOptions = options.joins[joinName];
76
+ const { table: joinTable, refCol } = options.joins[joinName];
77
+ return sql `"${raw(idCol)}" IN (SELECT "${raw(refCol)}"::${raw(schema.types[idCol])} FROM "${raw(joinTable)}" WHERE ${getNodeSql(ast[2], joinOptions)})`;
78
+ }
79
+ // It is a binary operator
80
+ const [prefix, ...suffix] = ast[1].split('.');
81
+ const { types = {} } = options.schema;
82
+ if (!types[prefix])
83
+ throw Error(`pg.no_column ${prefix}`);
84
+ if (types[prefix] === 'jsonb') {
85
+ const [lhs, textLhs] = suffix.length
86
+ ? [
87
+ sql `"${raw(prefix)}" #> ${suffix}`,
88
+ sql `"${raw(prefix)}" #>> ${suffix}`,
89
+ ]
90
+ : [sql `"${raw(prefix)}"`, sql `"${raw(prefix)}" #>> '{}'`];
91
+ return getBinarySql(lhs, 'jsonb', op, ast[2], textLhs);
92
+ }
93
+ if (suffix.length)
94
+ throw Error(`pg.lookup_not_jsonb ${prefix}`);
95
+ return getBinarySql(sql `"${raw(prefix)}"`, types[prefix], op, ast[2]);
96
+ }
97
+ export default function getSql(filter, options) {
98
+ const ast = getAst(filter);
99
+ return getNodeSql(ast, options);
100
+ }
@@ -0,0 +1,2 @@
1
+ export { default as filterObject } from './filterObject.js';
2
+ export { default as getFilterSql } from './getSql.js';
package/index.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @param {Partial<TableOpts> & {connection: any}} options
3
+ * @returns {Function}
4
+ */
5
+ export declare const pg: ({ connection, ...rawOptions }: {
6
+ [x: string]: any;
7
+ connection: any;
8
+ }) => (store: any) => void;
package/index.js ADDED
@@ -0,0 +1,68 @@
1
+ import { encodePath, merge, remove } from '@graffy/common';
2
+ import Db from "./Db.js";
3
+ /**
4
+ * @typedef {{
5
+ * table: string,
6
+ * idCol: string,
7
+ * verCol: string,
8
+ * joins: Record<string, Partial<TableOpts> & { refCol: string }>,
9
+ * schema?: any,
10
+ * verDefault?: string
11
+ * }} TableOpts
12
+ */
13
+ /**
14
+ * @param {string} name
15
+ * @param {Partial<TableOpts>} options
16
+ * @param {string} parentName
17
+ * @returns {TableOpts}
18
+ */
19
+ function getTableOpts(name, { table, idCol, verCol, joins, schema, verDefault } = {}, parentName = null) {
20
+ const tableName = table || name;
21
+ return {
22
+ table: table || name,
23
+ idCol: idCol || 'id',
24
+ verCol: verCol || 'updatedAt',
25
+ joins: Object.fromEntries(Object.entries(joins || {}).map(([joinName, { refCol = parentName, ...joinOptions }]) => [
26
+ joinName,
27
+ {
28
+ refCol,
29
+ ...getTableOpts(joinName, joinOptions, tableName),
30
+ },
31
+ ])),
32
+ schema,
33
+ verDefault,
34
+ };
35
+ }
36
+ /**
37
+ * @param {Partial<TableOpts> & {connection: any}} options
38
+ * @returns {Function}
39
+ */
40
+ export const pg = ({ connection, ...rawOptions }) => (store) => {
41
+ store.on('read', read);
42
+ store.on('write', write);
43
+ const prefix = store.path;
44
+ /** @type {TableOpts & {prefix?: string[]}} */
45
+ const tableOpts = getTableOpts(prefix[prefix.length - 1], rawOptions);
46
+ tableOpts.prefix = prefix;
47
+ const defaultDb = new Db(connection);
48
+ function read(query, options, next) {
49
+ const { pgClient } = options;
50
+ const db = pgClient ? new Db(pgClient) : defaultDb;
51
+ const readPromise = db.read(query, tableOpts);
52
+ const remainingQuery = remove(query, encodePath(prefix));
53
+ const nextPromise = next(remainingQuery);
54
+ return Promise.all([readPromise, nextPromise]).then(([readRes, nextRes]) => {
55
+ return merge(readRes, nextRes);
56
+ });
57
+ }
58
+ function write(change, options, next) {
59
+ const { pgClient } = options;
60
+ const db = pgClient ? new Db(pgClient) : defaultDb;
61
+ const writePromise = db.write(change, tableOpts);
62
+ const remainingChange = remove(change, encodePath(prefix));
63
+ const nextPromise = next(remainingChange);
64
+ return Promise.all([writePromise, nextPromise]).then(([writeRes, nextRes]) => {
65
+ return merge(writeRes, nextRes);
66
+ });
67
+ }
68
+ };
package/package.json CHANGED
@@ -2,23 +2,29 @@
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.19.0",
6
- "main": "./index.cjs",
5
+ "version": "0.19.1-alpha.1",
6
+ "main": "./cjs/index.js",
7
7
  "exports": {
8
- "import": "./index.mjs",
9
- "require": "./index.cjs"
8
+ ".": {
9
+ "import": "./index.js",
10
+ "types": "./index.d.ts"
11
+ },
12
+ "./*": {
13
+ "import": "./*.js",
14
+ "types": "./*.d.ts"
15
+ }
10
16
  },
11
- "module": "./index.mjs",
12
- "types": "./types/index.d.ts",
17
+ "types": "./index.d.ts",
13
18
  "repository": {
14
19
  "type": "git",
15
20
  "url": "git+https://github.com/usegraffy/graffy.git"
16
21
  },
17
22
  "license": "Apache-2.0",
18
23
  "dependencies": {
19
- "@graffy/common": "0.19.0",
20
- "debug": "^4.4.3",
21
- "sql-formatter": "^15.7.2"
24
+ "sql-formatter": "^15.7.2",
25
+ "@graffy/common": "0.19.1-alpha.1",
26
+ "sql-template-tag": "^5.2.1",
27
+ "debug": "^4.4.3"
22
28
  },
23
29
  "peerDependencies": {
24
30
  "pg": "^8.0.0"
@@ -0,0 +1,12 @@
1
+ import { Sql } from 'sql-template-tag';
2
+ export declare const getJsonBuildTrusted: (variadic: any) => Sql;
3
+ export declare const lookup: (prop: any, options: any) => Sql;
4
+ export declare const lookupNumeric: (prop: any) => Sql;
5
+ export declare const getSelectCols: (options: any, projection?: any) => Sql;
6
+ export declare function cubeLiteralSql(value: any): Sql;
7
+ export declare const getInsert: (rows: any, options: any) => {
8
+ cols: Sql;
9
+ vals: Sql;
10
+ updates: Sql;
11
+ };
12
+ export declare const getUpdates: (row: any, options: any) => Sql;