@reventlessdev/reventless-postgres 3.0.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/CHANGELOG.md +11 -0
- package/LICENSE +202 -0
- package/__mocks__/emptyModule.js +1 -0
- package/lib/bs/.compiler.log +2 -0
- package/lib/bs/ReventlessPostgres.cmi +0 -0
- package/lib/bs/ReventlessPostgres.cmj +0 -0
- package/lib/bs/ReventlessPostgres.cmt +0 -0
- package/lib/bs/ReventlessPostgres.mlmap +9 -0
- package/lib/bs/build.ninja +0 -0
- package/lib/bs/compiler-info.json +8 -0
- package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
- package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmj +0 -0
- package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
- package/lib/bs/src/DcbEventLogStorage_Postgres.ast +0 -0
- package/lib/bs/src/DcbEventLogStorage_Postgres.res +352 -0
- package/lib/bs/src/DcbEventLogStorage_Postgres.res.mjs +385 -0
- package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
- package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmj +0 -0
- package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
- package/lib/bs/src/EventLogStorage_Postgres.ast +0 -0
- package/lib/bs/src/EventLogStorage_Postgres.res +176 -0
- package/lib/bs/src/EventLogStorage_Postgres.res.mjs +207 -0
- package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmi +0 -0
- package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmj +0 -0
- package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmt +0 -0
- package/lib/bs/src/PgChangeFeed.ast +0 -0
- package/lib/bs/src/PgChangeFeed.res +145 -0
- package/lib/bs/src/PgChangeFeed.res.mjs +90 -0
- package/lib/bs/src/PgDriver-ReventlessPostgres.cmi +0 -0
- package/lib/bs/src/PgDriver-ReventlessPostgres.cmj +0 -0
- package/lib/bs/src/PgDriver-ReventlessPostgres.cmt +0 -0
- package/lib/bs/src/PgDriver-ReventlessPostgres.cmti +0 -0
- package/lib/bs/src/PgDriver.ast +0 -0
- package/lib/bs/src/PgDriver.iast +0 -0
- package/lib/bs/src/PgDriver.res +111 -0
- package/lib/bs/src/PgDriver.res.mjs +85 -0
- package/lib/bs/src/PgDriver.resi +68 -0
- package/lib/bs/src/PgSchema-ReventlessPostgres.cmi +0 -0
- package/lib/bs/src/PgSchema-ReventlessPostgres.cmj +0 -0
- package/lib/bs/src/PgSchema-ReventlessPostgres.cmt +0 -0
- package/lib/bs/src/PgSchema.ast +0 -0
- package/lib/bs/src/PgSchema.res +256 -0
- package/lib/bs/src/PgSchema.res.mjs +46 -0
- package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
- package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmj +0 -0
- package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
- package/lib/bs/src/QueryDbStorage_Postgres.ast +0 -0
- package/lib/bs/src/QueryDbStorage_Postgres.res +244 -0
- package/lib/bs/src/QueryDbStorage_Postgres.res.mjs +225 -0
- package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
- package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmj +0 -0
- package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
- package/lib/bs/src/QueryEnginePostgres.ast +0 -0
- package/lib/bs/src/QueryEnginePostgres.res +127 -0
- package/lib/bs/src/QueryEnginePostgres.res.mjs +172 -0
- package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmi +0 -0
- package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmj +0 -0
- package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmt +0 -0
- package/lib/bs/tests/PostgresIntegrationTest.ast +0 -0
- package/lib/bs/tests/PostgresIntegrationTest.res +236 -0
- package/lib/bs/tests/PostgresIntegrationTest.res.mjs +328 -0
- package/lib/ocaml/.compiler.log +2 -0
- package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/DcbEventLogStorage_Postgres.ast +0 -0
- package/lib/ocaml/DcbEventLogStorage_Postgres.res +352 -0
- package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/EventLogStorage_Postgres.ast +0 -0
- package/lib/ocaml/EventLogStorage_Postgres.res +176 -0
- package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/PgChangeFeed.ast +0 -0
- package/lib/ocaml/PgChangeFeed.res +145 -0
- package/lib/ocaml/PgDriver-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/PgDriver-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/PgDriver-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/PgDriver-ReventlessPostgres.cmti +0 -0
- package/lib/ocaml/PgDriver.ast +0 -0
- package/lib/ocaml/PgDriver.iast +0 -0
- package/lib/ocaml/PgDriver.res +111 -0
- package/lib/ocaml/PgDriver.resi +68 -0
- package/lib/ocaml/PgSchema-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/PgSchema-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/PgSchema-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/PgSchema.ast +0 -0
- package/lib/ocaml/PgSchema.res +256 -0
- package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/PostgresIntegrationTest.ast +0 -0
- package/lib/ocaml/PostgresIntegrationTest.res +236 -0
- package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/QueryDbStorage_Postgres.ast +0 -0
- package/lib/ocaml/QueryDbStorage_Postgres.res +244 -0
- package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/QueryEnginePostgres.ast +0 -0
- package/lib/ocaml/QueryEnginePostgres.res +127 -0
- package/lib/ocaml/ReventlessPostgres.cmi +0 -0
- package/lib/ocaml/ReventlessPostgres.cmj +0 -0
- package/lib/ocaml/ReventlessPostgres.cmt +0 -0
- package/lib/ocaml/ReventlessPostgres.mlmap +9 -0
- package/package.json +56 -0
- package/rescript.json +36 -0
- package/src/DcbEventLogStorage_Postgres.res +352 -0
- package/src/DcbEventLogStorage_Postgres.res.mjs +385 -0
- package/src/EventLogStorage_Postgres.res +176 -0
- package/src/EventLogStorage_Postgres.res.mjs +207 -0
- package/src/PgChangeFeed.res +145 -0
- package/src/PgChangeFeed.res.mjs +90 -0
- package/src/PgDriver.res +111 -0
- package/src/PgDriver.res.mjs +85 -0
- package/src/PgDriver.resi +68 -0
- package/src/PgSchema.res +256 -0
- package/src/PgSchema.res.mjs +46 -0
- package/src/QueryDbStorage_Postgres.res +244 -0
- package/src/QueryDbStorage_Postgres.res.mjs +225 -0
- package/src/QueryEnginePostgres.res +127 -0
- package/src/QueryEnginePostgres.res.mjs +172 -0
- package/tests/PostgresIntegrationTest.res +236 -0
- package/tests/PostgresIntegrationTest.res.mjs +328 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Stream from "effect/Stream";
|
|
7
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
8
|
+
import * as PgDriver$ReventlessPostgres from "./PgDriver.res.mjs";
|
|
9
|
+
|
|
10
|
+
function tableName(name) {
|
|
11
|
+
return "qdb_" + name.replaceAll("-", "_");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function sanitizeIdent(s) {
|
|
15
|
+
return s.replace(/[^A-Za-z0-9_]/g, "_");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let notExpiredClause = "(expires_at IS NULL OR expires_at > extract(epoch from now()))";
|
|
19
|
+
|
|
20
|
+
function jsonField(field) {
|
|
21
|
+
return `(item->>'` + field.replaceAll("'", "''") + `')`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function compositeExpr(fields, sep) {
|
|
25
|
+
let escapedSep = sep.replaceAll("'", "''");
|
|
26
|
+
return fields.map(jsonField).join(` || '` + escapedSep + `' || `);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function indexExpressions(idx) {
|
|
30
|
+
let fields = idx.pkFields;
|
|
31
|
+
let pkExpr;
|
|
32
|
+
let exit = 0;
|
|
33
|
+
if (fields !== undefined && fields.length !== 0) {
|
|
34
|
+
pkExpr = compositeExpr(fields, Stdlib_Option.getOr(idx.pkSep, "/"));
|
|
35
|
+
} else {
|
|
36
|
+
exit = 1;
|
|
37
|
+
}
|
|
38
|
+
if (exit === 1) {
|
|
39
|
+
let field = idx.idField;
|
|
40
|
+
pkExpr = field !== undefined ? jsonField(field) : jsonField(idx.index);
|
|
41
|
+
}
|
|
42
|
+
let fields$1 = idx.skFields;
|
|
43
|
+
let skExpr;
|
|
44
|
+
let exit$1 = 0;
|
|
45
|
+
if (fields$1 !== undefined && fields$1.length !== 0) {
|
|
46
|
+
skExpr = compositeExpr(fields$1, Stdlib_Option.getOr(idx.skSep, "/"));
|
|
47
|
+
} else {
|
|
48
|
+
exit$1 = 1;
|
|
49
|
+
}
|
|
50
|
+
if (exit$1 === 1) {
|
|
51
|
+
let field$1 = idx.subIdField;
|
|
52
|
+
skExpr = field$1 !== undefined ? jsonField(field$1) : undefined;
|
|
53
|
+
}
|
|
54
|
+
return [
|
|
55
|
+
pkExpr,
|
|
56
|
+
skExpr
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function ensureTable(pool, table) {
|
|
61
|
+
return await PgDriver$ReventlessPostgres.exec(pool, `CREATE TABLE IF NOT EXISTS ` + table + ` (partition_key text NOT NULL, sub_key text NOT NULL DEFAULT '', item jsonb NOT NULL, expires_at double precision, PRIMARY KEY (partition_key, sub_key))`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function ensureIndexes(pool, table, indexes) {
|
|
65
|
+
for (let i = 0, i_finish = indexes.length; i < i_finish; ++i) {
|
|
66
|
+
let idx = indexes[i];
|
|
67
|
+
let match = indexExpressions(idx);
|
|
68
|
+
let skExprOpt = match[1];
|
|
69
|
+
let pkExpr = match[0];
|
|
70
|
+
let indexName = `idx_` + table + `_` + sanitizeIdent(idx.index);
|
|
71
|
+
let cols = skExprOpt !== undefined ? pkExpr + `, ` + skExprOpt : pkExpr;
|
|
72
|
+
await PgDriver$ReventlessPostgres.exec(pool, `CREATE INDEX IF NOT EXISTS ` + indexName + ` ON ` + table + ` (` + cols + `)`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function decodeItem(row) {
|
|
77
|
+
return Stdlib_Option.getOr(row["item"], null);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function computeSubKey(item, subIdField) {
|
|
81
|
+
if (subIdField === undefined) {
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
let match = Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(item), o => o[subIdField]);
|
|
85
|
+
if (typeof match === "string") {
|
|
86
|
+
return match;
|
|
87
|
+
} else {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function withId(item, partitionKey) {
|
|
93
|
+
let obj = Stdlib_JSON.Decode.object(item);
|
|
94
|
+
if (obj === undefined) {
|
|
95
|
+
return item;
|
|
96
|
+
}
|
|
97
|
+
if (!Stdlib_Option.isNone(obj["id"])) {
|
|
98
|
+
return item;
|
|
99
|
+
}
|
|
100
|
+
let copy = Object.assign({}, obj);
|
|
101
|
+
copy["id"] = partitionKey;
|
|
102
|
+
return copy;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function makeStorage(pool, name, indexes, subIdField) {
|
|
106
|
+
let table = tableName(name);
|
|
107
|
+
let ready = ensureTable(pool, table).then(() => ensureIndexes(pool, table, indexes));
|
|
108
|
+
let rowsFor = async id => {
|
|
109
|
+
await ready;
|
|
110
|
+
return (await PgDriver$ReventlessPostgres.query(pool, `SELECT item FROM ` + table + ` WHERE partition_key = $1 AND ` + notExpiredClause + ` ORDER BY sub_key ASC`, [id])).map(decodeItem);
|
|
111
|
+
};
|
|
112
|
+
let saveOne = async (id, state, ttl) => {
|
|
113
|
+
await ready;
|
|
114
|
+
let subKey = computeSubKey(state, subIdField);
|
|
115
|
+
let expiresAt = ttl !== undefined ? ttl : null;
|
|
116
|
+
await PgDriver$ReventlessPostgres.query(pool, `INSERT INTO ` + table + `(partition_key, sub_key, item, expires_at)
|
|
117
|
+
VALUES ($1, $2, $3::jsonb, $4)
|
|
118
|
+
ON CONFLICT (partition_key, sub_key)
|
|
119
|
+
DO UPDATE SET item = EXCLUDED.item, expires_at = EXCLUDED.expires_at`, [
|
|
120
|
+
id,
|
|
121
|
+
subKey,
|
|
122
|
+
JSON.stringify(state),
|
|
123
|
+
expiresAt
|
|
124
|
+
]);
|
|
125
|
+
};
|
|
126
|
+
let load = async id => ({
|
|
127
|
+
TAG: "Ok",
|
|
128
|
+
_0: await rowsFor(id)
|
|
129
|
+
});
|
|
130
|
+
let loadStream = id => Stream.flatMap(Stream.fromEffect(Effect.promise(() => rowsFor(id))), arr => Stream.fromIterable(arr));
|
|
131
|
+
let save = async (id, state, _saveMode, ttl) => {
|
|
132
|
+
await saveOne(id, state, ttl);
|
|
133
|
+
return {
|
|
134
|
+
TAG: "Ok",
|
|
135
|
+
_0: undefined
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
let saveBatch = async batch => {
|
|
139
|
+
for (let i = 0, i_finish = batch.length; i < i_finish; ++i) {
|
|
140
|
+
let match = batch[i];
|
|
141
|
+
await saveOne(match[0], match[1], match[2]);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
TAG: "Ok",
|
|
145
|
+
_0: undefined
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
let count = async (id, fieldName, inc) => {
|
|
149
|
+
await ready;
|
|
150
|
+
let existing = Stdlib_Option.flatMap((await rowsFor(id))[0], Stdlib_JSON.Decode.object);
|
|
151
|
+
let current = Stdlib_Option.mapOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(existing, o => o[fieldName]), Stdlib_JSON.Decode.float), 0, prim => prim | 0);
|
|
152
|
+
let next = current + inc | 0;
|
|
153
|
+
let obj = existing !== undefined ? Object.assign({}, existing) : ({});
|
|
154
|
+
obj["id"] = id;
|
|
155
|
+
obj[fieldName] = next;
|
|
156
|
+
await saveOne(id, obj, undefined);
|
|
157
|
+
return {
|
|
158
|
+
TAG: "Ok",
|
|
159
|
+
_0: next
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
let $$delete = async (id, subIdOpt) => {
|
|
163
|
+
await ready;
|
|
164
|
+
if (subIdOpt !== undefined) {
|
|
165
|
+
await PgDriver$ReventlessPostgres.query(pool, `DELETE FROM ` + table + ` WHERE partition_key = $1 AND sub_key = $2`, [
|
|
166
|
+
id,
|
|
167
|
+
subIdOpt[1]
|
|
168
|
+
]);
|
|
169
|
+
} else {
|
|
170
|
+
await PgDriver$ReventlessPostgres.query(pool, `DELETE FROM ` + table + ` WHERE partition_key = $1`, [id]);
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
TAG: "Ok",
|
|
174
|
+
_0: undefined
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
let deleteBatch = async ids => {
|
|
178
|
+
for (let i = 0, i_finish = ids.length; i < i_finish; ++i) {
|
|
179
|
+
let match = ids[i];
|
|
180
|
+
await $$delete(match[0], match[1]);
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
TAG: "Ok",
|
|
184
|
+
_0: undefined
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
let ops = {
|
|
188
|
+
load: load,
|
|
189
|
+
loadStream: loadStream,
|
|
190
|
+
save: save,
|
|
191
|
+
saveBatch: saveBatch,
|
|
192
|
+
count: count,
|
|
193
|
+
delete: $$delete,
|
|
194
|
+
deleteBatch: deleteBatch
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
resources: [],
|
|
198
|
+
dataSourceName: Pulumi.output(""),
|
|
199
|
+
operations: Pulumi.output(ops)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function Make(P) {
|
|
204
|
+
let make = (name, indexes, subIdField, param, param$1, param$2, param$3) => makeStorage(P.pool, name, indexes, subIdField);
|
|
205
|
+
return {
|
|
206
|
+
make: make
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export {
|
|
211
|
+
tableName,
|
|
212
|
+
sanitizeIdent,
|
|
213
|
+
notExpiredClause,
|
|
214
|
+
jsonField,
|
|
215
|
+
compositeExpr,
|
|
216
|
+
indexExpressions,
|
|
217
|
+
ensureTable,
|
|
218
|
+
ensureIndexes,
|
|
219
|
+
decodeItem,
|
|
220
|
+
computeSubKey,
|
|
221
|
+
withId,
|
|
222
|
+
makeStorage,
|
|
223
|
+
Make,
|
|
224
|
+
}
|
|
225
|
+
/* effect/Effect Not a pure module */
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Postgres QueryEngine — compiles the QueryEngine query/scan AST to SQL over the
|
|
2
|
+
// `qdb_<name>` JSONB tables (QueryDbStorage_Postgres). The reventless-local
|
|
3
|
+
// LocalQueryEngine reads from in-process bus registries and ignores filters; this
|
|
4
|
+
// one pushes filters down to SQL, the way a production engine must.
|
|
5
|
+
|
|
6
|
+
open ReventlessCore
|
|
7
|
+
open Reventless
|
|
8
|
+
|
|
9
|
+
let valueToText = (v: QueryEngine.value): string =>
|
|
10
|
+
switch v {
|
|
11
|
+
| QueryEngine.String(s) => s
|
|
12
|
+
| QueryEngine.Int(i) => i->Int.toString
|
|
13
|
+
| QueryEngine.Bool(b) => b ? "true" : "false"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let isNumeric = (v: QueryEngine.value): bool =>
|
|
17
|
+
switch v {
|
|
18
|
+
| QueryEngine.Int(_) => true
|
|
19
|
+
| _ => false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type paramBuilder = {mutable params: array<JSON.t>}
|
|
23
|
+
let param = (b: paramBuilder, v: JSON.t): string => {
|
|
24
|
+
b.params->Array.push(v)
|
|
25
|
+
"$" ++ Int.toString(b.params->Array.length)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let col = (field: string): string => `(item->>'${field->String.replaceAll("'", "''")}')`
|
|
29
|
+
|
|
30
|
+
// One Filter.config → SQL predicate, appending any bound params.
|
|
31
|
+
let filterSql = (b: paramBuilder, field: string, cmp: QueryEngine.Filter.comparator, v: QueryEngine.value): string => {
|
|
32
|
+
let c = col(field)
|
|
33
|
+
let bind = () => b->param(JSON.Encode.string(valueToText(v)))
|
|
34
|
+
let cmpSql = op =>
|
|
35
|
+
isNumeric(v) ? `${c}::numeric ${op} ${bind()}::numeric` : `${c} ${op} ${bind()}`
|
|
36
|
+
switch cmp {
|
|
37
|
+
| QueryEngine.Filter.Equal => cmpSql("=")
|
|
38
|
+
| Unequal => cmpSql("<>")
|
|
39
|
+
| LessOrEqual => cmpSql("<=")
|
|
40
|
+
| Less => cmpSql("<")
|
|
41
|
+
| GreaterOrEqual => cmpSql(">=")
|
|
42
|
+
| Greater => cmpSql(">")
|
|
43
|
+
| Exists => `(item ? '${field->String.replaceAll("'", "''")}')`
|
|
44
|
+
| NotExists => `NOT (item ? '${field->String.replaceAll("'", "''")}')`
|
|
45
|
+
| Contains => `strpos(${c}, ${bind()}) > 0`
|
|
46
|
+
| NotContains => `(${c} IS NULL OR strpos(${c}, ${bind()}) = 0)`
|
|
47
|
+
| BeginsWith => `starts_with(${c}, ${bind()})`
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let subIdSql = (b: paramBuilder, field: string, cmp: QueryEngine.SubId.comparator, v: QueryEngine.value): string => {
|
|
52
|
+
let c = col(field)
|
|
53
|
+
let bind = () => b->param(JSON.Encode.string(valueToText(v)))
|
|
54
|
+
let cmpSql = op => isNumeric(v) ? `${c}::numeric ${op} ${bind()}::numeric` : `${c} ${op} ${bind()}`
|
|
55
|
+
switch cmp {
|
|
56
|
+
| QueryEngine.SubId.Equal => cmpSql("=")
|
|
57
|
+
| Unequal => cmpSql("<>")
|
|
58
|
+
| LessOrEqual => cmpSql("<=")
|
|
59
|
+
| Less => cmpSql("<")
|
|
60
|
+
| GreaterOrEqual => cmpSql(">=")
|
|
61
|
+
| Greater => cmpSql(">")
|
|
62
|
+
| BeginsWith => `starts_with(${c}, ${bind()})`
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let notExpired = QueryDbStorage_Postgres.notExpiredClause
|
|
67
|
+
|
|
68
|
+
// Add `id` (from partition_key) when absent — matches allRows/scan in the SQLite
|
|
69
|
+
// engine; partition-key queries return items as stored.
|
|
70
|
+
let decodeRow = (~withId, row: dict<JSON.t>): JSON.t => {
|
|
71
|
+
let item = QueryDbStorage_Postgres.decodeItem(row)
|
|
72
|
+
if withId {
|
|
73
|
+
let pk = switch row->Dict.get("partition_key") {
|
|
74
|
+
| Some(JSON.String(s)) => s
|
|
75
|
+
| _ => ""
|
|
76
|
+
}
|
|
77
|
+
QueryDbStorage_Postgres.withId(item, pk)
|
|
78
|
+
} else {
|
|
79
|
+
item
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module Make = (P: {let pool: PgDriver.pool}) => {
|
|
84
|
+
let query = async (
|
|
85
|
+
~readModelName,
|
|
86
|
+
~key=?,
|
|
87
|
+
~id: QueryEngine.value,
|
|
88
|
+
~subIdConfig=?,
|
|
89
|
+
~filterConfigs=?,
|
|
90
|
+
~ascending=?,
|
|
91
|
+
~limit=?,
|
|
92
|
+
): array<JSON.t> => {
|
|
93
|
+
let table = QueryDbStorage_Postgres.tableName(readModelName)
|
|
94
|
+
let keyStr = switch key {
|
|
95
|
+
| Some(k) => k
|
|
96
|
+
| None => valueToText(id)
|
|
97
|
+
}
|
|
98
|
+
let b = {params: []}
|
|
99
|
+
let where = [`partition_key = ${b->param(JSON.Encode.string(keyStr))}`, notExpired]
|
|
100
|
+
switch subIdConfig {
|
|
101
|
+
| Some((field, cmp, v)) => where->Array.push(subIdSql(b, field, cmp, v))
|
|
102
|
+
| None => ()
|
|
103
|
+
}
|
|
104
|
+
filterConfigs
|
|
105
|
+
->Option.getOr([])
|
|
106
|
+
->Array.forEach(((field, cmp, v)) => where->Array.push(filterSql(b, field, cmp, v)))
|
|
107
|
+
let dir = ascending->Option.getOr(true) ? "ASC" : "DESC"
|
|
108
|
+
let limitClause = switch limit {
|
|
109
|
+
| Some(n) => ` LIMIT ${b->param(JSON.Encode.int(n))}`
|
|
110
|
+
| None => ""
|
|
111
|
+
}
|
|
112
|
+
let sql = `SELECT partition_key, item FROM ${table} WHERE ${where->Array.join(" AND ")} ORDER BY sub_key ${dir}${limitClause}`
|
|
113
|
+
(await P.pool->PgDriver.query(sql, b.params))->Array.map(decodeRow(~withId=false, ...))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let scan = async (~readModelName, ~filterConfigs, ~limit): array<JSON.t> => {
|
|
117
|
+
let table = QueryDbStorage_Postgres.tableName(readModelName)
|
|
118
|
+
let b = {params: []}
|
|
119
|
+
let where = [notExpired]
|
|
120
|
+
filterConfigs->Array.forEach(((field, cmp, v)) => where->Array.push(filterSql(b, field, cmp, v)))
|
|
121
|
+
let sql = `SELECT partition_key, item FROM ${table} WHERE ${where->Array.join(" AND ")} ORDER BY partition_key, sub_key LIMIT ${b->param(JSON.Encode.int(limit))}`
|
|
122
|
+
(await P.pool->PgDriver.query(sql, b.params))->Array.map(decodeRow(~withId=true, ...))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let make: QueryDb_Adapter.queryEngineMaker = _allQueryDbs =>
|
|
126
|
+
Pulumi.Output.make({QueryEngine.scan, query})
|
|
127
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
5
|
+
import * as PgDriver$ReventlessPostgres from "./PgDriver.res.mjs";
|
|
6
|
+
import * as QueryDbStorage_Postgres$ReventlessPostgres from "./QueryDbStorage_Postgres.res.mjs";
|
|
7
|
+
|
|
8
|
+
function valueToText(v) {
|
|
9
|
+
switch (v.TAG) {
|
|
10
|
+
case "String" :
|
|
11
|
+
return v._0;
|
|
12
|
+
case "Int" :
|
|
13
|
+
return v._0.toString();
|
|
14
|
+
case "Bool" :
|
|
15
|
+
if (v._0) {
|
|
16
|
+
return "true";
|
|
17
|
+
} else {
|
|
18
|
+
return "false";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isNumeric(v) {
|
|
24
|
+
switch (v.TAG) {
|
|
25
|
+
case "Int" :
|
|
26
|
+
return true;
|
|
27
|
+
case "String" :
|
|
28
|
+
case "Bool" :
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function param(b, v) {
|
|
34
|
+
b.params.push(v);
|
|
35
|
+
return "$" + b.params.length.toString();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function col(field) {
|
|
39
|
+
return `(item->>'` + field.replaceAll("'", "''") + `')`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function filterSql(b, field, cmp, v) {
|
|
43
|
+
let c = col(field);
|
|
44
|
+
let cmpSql = op => {
|
|
45
|
+
if (isNumeric(v)) {
|
|
46
|
+
return c + `::numeric ` + op + ` ` + param(b, valueToText(v)) + `::numeric`;
|
|
47
|
+
} else {
|
|
48
|
+
return c + ` ` + op + ` ` + param(b, valueToText(v));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
switch (cmp) {
|
|
52
|
+
case "Equal" :
|
|
53
|
+
return cmpSql("=");
|
|
54
|
+
case "Unequal" :
|
|
55
|
+
return cmpSql("<>");
|
|
56
|
+
case "LessOrEqual" :
|
|
57
|
+
return cmpSql("<=");
|
|
58
|
+
case "Less" :
|
|
59
|
+
return cmpSql("<");
|
|
60
|
+
case "GreaterOrEqual" :
|
|
61
|
+
return cmpSql(">=");
|
|
62
|
+
case "Greater" :
|
|
63
|
+
return cmpSql(">");
|
|
64
|
+
case "Exists" :
|
|
65
|
+
return `(item ? '` + field.replaceAll("'", "''") + `')`;
|
|
66
|
+
case "NotExists" :
|
|
67
|
+
return `NOT (item ? '` + field.replaceAll("'", "''") + `')`;
|
|
68
|
+
case "Contains" :
|
|
69
|
+
return `strpos(` + c + `, ` + param(b, valueToText(v)) + `) > 0`;
|
|
70
|
+
case "NotContains" :
|
|
71
|
+
return `(` + c + ` IS NULL OR strpos(` + c + `, ` + param(b, valueToText(v)) + `) = 0)`;
|
|
72
|
+
case "BeginsWith" :
|
|
73
|
+
return `starts_with(` + c + `, ` + param(b, valueToText(v)) + `)`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function subIdSql(b, field, cmp, v) {
|
|
78
|
+
let c = col(field);
|
|
79
|
+
let cmpSql = op => {
|
|
80
|
+
if (isNumeric(v)) {
|
|
81
|
+
return c + `::numeric ` + op + ` ` + param(b, valueToText(v)) + `::numeric`;
|
|
82
|
+
} else {
|
|
83
|
+
return c + ` ` + op + ` ` + param(b, valueToText(v));
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
switch (cmp) {
|
|
87
|
+
case "Equal" :
|
|
88
|
+
return cmpSql("=");
|
|
89
|
+
case "Unequal" :
|
|
90
|
+
return cmpSql("<>");
|
|
91
|
+
case "LessOrEqual" :
|
|
92
|
+
return cmpSql("<=");
|
|
93
|
+
case "Less" :
|
|
94
|
+
return cmpSql("<");
|
|
95
|
+
case "GreaterOrEqual" :
|
|
96
|
+
return cmpSql(">=");
|
|
97
|
+
case "Greater" :
|
|
98
|
+
return cmpSql(">");
|
|
99
|
+
case "BeginsWith" :
|
|
100
|
+
return `starts_with(` + c + `, ` + param(b, valueToText(v)) + `)`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function decodeRow(withId, row) {
|
|
105
|
+
let item = QueryDbStorage_Postgres$ReventlessPostgres.decodeItem(row);
|
|
106
|
+
if (!withId) {
|
|
107
|
+
return item;
|
|
108
|
+
}
|
|
109
|
+
let match = row["partition_key"];
|
|
110
|
+
let pk = typeof match === "string" ? match : "";
|
|
111
|
+
return QueryDbStorage_Postgres$ReventlessPostgres.withId(item, pk);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function Make(P) {
|
|
115
|
+
let query = async (readModelName, key, id, subIdConfig, filterConfigs, ascending, limit) => {
|
|
116
|
+
let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
|
|
117
|
+
let keyStr = key !== undefined ? key : valueToText(id);
|
|
118
|
+
let b = {
|
|
119
|
+
params: []
|
|
120
|
+
};
|
|
121
|
+
let where = [
|
|
122
|
+
`partition_key = ` + param(b, keyStr),
|
|
123
|
+
QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause
|
|
124
|
+
];
|
|
125
|
+
if (subIdConfig !== undefined) {
|
|
126
|
+
where.push(subIdSql(b, subIdConfig[0], subIdConfig[1], subIdConfig[2]));
|
|
127
|
+
}
|
|
128
|
+
Stdlib_Option.getOr(filterConfigs, []).forEach(param => {
|
|
129
|
+
where.push(filterSql(b, param[0], param[1], param[2]));
|
|
130
|
+
});
|
|
131
|
+
let dir = Stdlib_Option.getOr(ascending, true) ? "ASC" : "DESC";
|
|
132
|
+
let limitClause = limit !== undefined ? ` LIMIT ` + param(b, limit) : "";
|
|
133
|
+
let sql = `SELECT partition_key, item FROM ` + table + ` WHERE ` + where.join(" AND ") + ` ORDER BY sub_key ` + dir + limitClause;
|
|
134
|
+
return (await PgDriver$ReventlessPostgres.query(P.pool, sql, b.params)).map(extra => decodeRow(false, extra));
|
|
135
|
+
};
|
|
136
|
+
let scan = async (readModelName, filterConfigs, limit) => {
|
|
137
|
+
let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
|
|
138
|
+
let b = {
|
|
139
|
+
params: []
|
|
140
|
+
};
|
|
141
|
+
let where = [QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause];
|
|
142
|
+
filterConfigs.forEach(param => {
|
|
143
|
+
where.push(filterSql(b, param[0], param[1], param[2]));
|
|
144
|
+
});
|
|
145
|
+
let sql = `SELECT partition_key, item FROM ` + table + ` WHERE ` + where.join(" AND ") + ` ORDER BY partition_key, sub_key LIMIT ` + param(b, limit);
|
|
146
|
+
return (await PgDriver$ReventlessPostgres.query(P.pool, sql, b.params)).map(extra => decodeRow(true, extra));
|
|
147
|
+
};
|
|
148
|
+
let make = _allQueryDbs => Pulumi.output({
|
|
149
|
+
scan: scan,
|
|
150
|
+
query: query
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
query: query,
|
|
154
|
+
scan: scan,
|
|
155
|
+
make: make
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let notExpired = QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause;
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
valueToText,
|
|
163
|
+
isNumeric,
|
|
164
|
+
param,
|
|
165
|
+
col,
|
|
166
|
+
filterSql,
|
|
167
|
+
subIdSql,
|
|
168
|
+
notExpired,
|
|
169
|
+
decodeRow,
|
|
170
|
+
Make,
|
|
171
|
+
}
|
|
172
|
+
/* @pulumi/pulumi Not a pure module */
|