@reventlessdev/reventless-postgres 3.0.0-alpha.10 → 3.0.0-alpha.101

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.
Files changed (117) hide show
  1. package/CHANGELOG.md +731 -0
  2. package/README.md +83 -0
  3. package/lib/bs/.compiler.log +2 -2
  4. package/lib/bs/ReventlessPostgres.cmt +0 -0
  5. package/lib/bs/ReventlessPostgres.mlmap +0 -2
  6. package/lib/bs/compiler-info.json +2 -2
  7. package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  8. package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  9. package/lib/bs/src/DcbEventLogStorage_Postgres.ast +0 -0
  10. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  11. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  12. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops.ast +0 -0
  13. package/lib/bs/src/EventLogChangeFeed-ReventlessPostgres.cmi +0 -0
  14. package/lib/bs/src/EventLogChangeFeed-ReventlessPostgres.cmt +0 -0
  15. package/lib/bs/src/EventLogChangeFeed.ast +0 -0
  16. package/lib/bs/src/EventLogChangeFeed.res +5 -2
  17. package/lib/bs/src/EventLogChangeFeed.res.mjs +3 -3
  18. package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  19. package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  20. package/lib/bs/src/EventLogStorage_Postgres.ast +0 -0
  21. package/lib/bs/src/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  22. package/lib/bs/src/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  23. package/lib/bs/src/EventLogStorage_Postgres_Ops.ast +0 -0
  24. package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmi +0 -0
  25. package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmt +0 -0
  26. package/lib/bs/src/PgChangeFeed.ast +0 -0
  27. package/lib/bs/src/PgChangeFeed.res +6 -1
  28. package/lib/bs/src/PgChangeFeed.res.mjs +6 -6
  29. package/lib/bs/src/PgDriver-ReventlessPostgres.cmi +0 -0
  30. package/lib/bs/src/PgDriver-ReventlessPostgres.cmt +0 -0
  31. package/lib/bs/src/PgDriver-ReventlessPostgres.cmti +0 -0
  32. package/lib/bs/src/PgDriver.ast +0 -0
  33. package/lib/bs/src/PgDriver.iast +0 -0
  34. package/lib/bs/src/PgSchema-ReventlessPostgres.cmi +0 -0
  35. package/lib/bs/src/PgSchema-ReventlessPostgres.cmt +0 -0
  36. package/lib/bs/src/PgSchema.ast +0 -0
  37. package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
  38. package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
  39. package/lib/bs/src/QueryDbStorage_Postgres.ast +0 -0
  40. package/lib/bs/src/QueryDbStorage_Postgres.res +1 -1
  41. package/lib/bs/src/QueryDbStorage_Postgres.res.mjs +1 -1
  42. package/lib/bs/src/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  43. package/lib/bs/src/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  44. package/lib/bs/src/QueryDbStorage_Postgres_Ops.ast +0 -0
  45. package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
  46. package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
  47. package/lib/bs/src/QueryEnginePostgres.ast +0 -0
  48. package/lib/bs/src/QueryEnginePostgres.res +69 -12
  49. package/lib/bs/src/QueryEnginePostgres.res.mjs +49 -26
  50. package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmi +0 -0
  51. package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmt +0 -0
  52. package/lib/bs/tests/PostgresIntegrationTest.ast +0 -0
  53. package/lib/bs/tests/QueryEnginePostgresListPageTest-ReventlessPostgres.cmi +0 -0
  54. package/lib/bs/tests/QueryEnginePostgresListPageTest-ReventlessPostgres.cmt +0 -0
  55. package/lib/bs/tests/QueryEnginePostgresListPageTest.ast +0 -0
  56. package/lib/bs/tests/QueryEnginePostgresListPageTest.res.mjs +4 -4
  57. package/lib/ocaml/.compiler.log +2 -2
  58. package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  59. package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  60. package/lib/ocaml/DcbEventLogStorage_Postgres.ast +0 -0
  61. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  62. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  63. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops.ast +0 -0
  64. package/lib/ocaml/EventLogChangeFeed-ReventlessPostgres.cmi +0 -0
  65. package/lib/ocaml/EventLogChangeFeed-ReventlessPostgres.cmt +0 -0
  66. package/lib/ocaml/EventLogChangeFeed.ast +0 -0
  67. package/lib/ocaml/EventLogChangeFeed.res +5 -2
  68. package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  69. package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  70. package/lib/ocaml/EventLogStorage_Postgres.ast +0 -0
  71. package/lib/ocaml/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  72. package/lib/ocaml/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  73. package/lib/ocaml/EventLogStorage_Postgres_Ops.ast +0 -0
  74. package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmi +0 -0
  75. package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmt +0 -0
  76. package/lib/ocaml/PgChangeFeed.ast +0 -0
  77. package/lib/ocaml/PgChangeFeed.res +6 -1
  78. package/lib/ocaml/PgDriver-ReventlessPostgres.cmi +0 -0
  79. package/lib/ocaml/PgDriver-ReventlessPostgres.cmt +0 -0
  80. package/lib/ocaml/PgDriver-ReventlessPostgres.cmti +0 -0
  81. package/lib/ocaml/PgDriver.ast +0 -0
  82. package/lib/ocaml/PgDriver.iast +0 -0
  83. package/lib/ocaml/PgSchema-ReventlessPostgres.cmi +0 -0
  84. package/lib/ocaml/PgSchema-ReventlessPostgres.cmt +0 -0
  85. package/lib/ocaml/PgSchema.ast +0 -0
  86. package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
  87. package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
  88. package/lib/ocaml/QueryDbStorage_Postgres.ast +0 -0
  89. package/lib/ocaml/QueryDbStorage_Postgres.res +1 -1
  90. package/lib/ocaml/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  91. package/lib/ocaml/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  92. package/lib/ocaml/QueryDbStorage_Postgres_Ops.ast +0 -0
  93. package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
  94. package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
  95. package/lib/ocaml/QueryEnginePostgres.ast +0 -0
  96. package/lib/ocaml/QueryEnginePostgres.res +69 -12
  97. package/lib/ocaml/ReventlessPostgres.cmt +0 -0
  98. package/package.json +11 -11
  99. package/rescript.json +2 -1
  100. package/src/EventLogChangeFeed.res +5 -2
  101. package/src/EventLogChangeFeed.res.mjs +3 -3
  102. package/src/PgChangeFeed.res +6 -1
  103. package/src/PgChangeFeed.res.mjs +6 -6
  104. package/src/QueryDbStorage_Postgres.res +1 -1
  105. package/src/QueryDbStorage_Postgres.res.mjs +1 -1
  106. package/src/QueryEnginePostgres.res +69 -12
  107. package/src/QueryEnginePostgres.res.mjs +49 -26
  108. package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmi +0 -0
  109. package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmj +0 -0
  110. package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmt +0 -0
  111. package/lib/ocaml/PostgresIntegrationTest.ast +0 -0
  112. package/lib/ocaml/QueryEnginePostgresListPageTest-ReventlessPostgres.cmi +0 -0
  113. package/lib/ocaml/QueryEnginePostgresListPageTest-ReventlessPostgres.cmj +0 -0
  114. package/lib/ocaml/QueryEnginePostgresListPageTest-ReventlessPostgres.cmt +0 -0
  115. package/lib/ocaml/QueryEnginePostgresListPageTest.ast +0 -0
  116. package/tests/PostgresIntegrationTest.res.mjs +0 -377
  117. package/tests/QueryEnginePostgresListPageTest.res.mjs +0 -461
@@ -2,11 +2,10 @@
2
2
 
3
3
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
- import * as Pulumi from "@pulumi/pulumi";
6
5
  import * as Stdlib_Nullable from "@rescript/runtime/lib/es6/Stdlib_Nullable.js";
7
6
  import * as PgDriver$ReventlessPostgres from "./PgDriver.res.mjs";
8
7
  import * as QueryDbListQuery$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/QueryDbListQuery.res.mjs";
9
- import * as QueryDbStorage_Postgres$ReventlessPostgres from "./QueryDbStorage_Postgres.res.mjs";
8
+ import * as QueryDbStorage_Postgres_Ops$ReventlessPostgres from "./QueryDbStorage_Postgres_Ops.res.mjs";
10
9
 
11
10
  function valueToText(v) {
12
11
  switch (v.TAG) {
@@ -105,25 +104,25 @@ function subIdSql(b, field, cmp, v) {
105
104
  }
106
105
 
107
106
  function decodeRow(withId, row) {
108
- let item = QueryDbStorage_Postgres$ReventlessPostgres.decodeItem(row);
107
+ let item = QueryDbStorage_Postgres_Ops$ReventlessPostgres.decodeItem(row);
109
108
  if (!withId) {
110
109
  return item;
111
110
  }
112
111
  let match = row["partition_key"];
113
112
  let pk = typeof match === "string" ? match : "";
114
- return QueryDbStorage_Postgres$ReventlessPostgres.withId(item, pk);
113
+ return QueryDbStorage_Postgres_Ops$ReventlessPostgres.withId(item, pk);
115
114
  }
116
115
 
117
116
  function Make(P) {
118
117
  let query = async (readModelName, key, id, subIdConfig, filterConfigs, ascending, limit) => {
119
- let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
118
+ let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
120
119
  let keyStr = key !== undefined ? key : valueToText(id);
121
120
  let b = {
122
121
  params: []
123
122
  };
124
123
  let where = [
125
124
  `partition_key = ` + param(b, keyStr),
126
- QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause
125
+ QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause
127
126
  ];
128
127
  if (subIdConfig !== undefined) {
129
128
  where.push(subIdSql(b, subIdConfig[0], subIdConfig[1], subIdConfig[2]));
@@ -137,11 +136,11 @@ function Make(P) {
137
136
  return (await PgDriver$ReventlessPostgres.query(P.pool, sql, b.params)).map(extra => decodeRow(false, extra));
138
137
  };
139
138
  let scan = async (readModelName, filterConfigs, limit) => {
140
- let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
139
+ let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
141
140
  let b = {
142
141
  params: []
143
142
  };
144
- let where = [QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause];
143
+ let where = [QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause];
145
144
  filterConfigs.forEach(param => {
146
145
  where.push(filterSql(b, param[0], param[1], param[2]));
147
146
  });
@@ -149,29 +148,45 @@ function Make(P) {
149
148
  return (await PgDriver$ReventlessPostgres.query(P.pool, sql, b.params)).map(extra => decodeRow(true, extra));
150
149
  };
151
150
  let indexLookup = async (readModelName, field, value) => {
152
- let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
151
+ let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
153
152
  let b = {
154
153
  params: []
155
154
  };
156
- let sql = `SELECT partition_key, item FROM ` + table + ` WHERE ` + col(field) + ` = ` + param(b, value) + ` AND ` + QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause;
155
+ let sql = `SELECT partition_key, item FROM ` + table + ` WHERE ` + col(field) + ` = ` + param(b, value) + ` AND ` + QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause;
157
156
  return (await PgDriver$ReventlessPostgres.query(P.pool, sql, b.params)).map(extra => decodeRow(true, extra));
158
157
  };
159
158
  let byIds = async (readModelName, ids) => {
160
159
  if (ids.length === 0) {
161
160
  return [];
162
161
  }
163
- let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
162
+ let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
164
163
  let b = {
165
164
  params: []
166
165
  };
167
166
  let idsJson = ids.map(prim => prim);
168
- let sql = `SELECT partition_key, item FROM ` + table + ` WHERE partition_key = ANY(` + param(b, idsJson) + `::text[]) AND ` + QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause;
167
+ let sql = `SELECT partition_key, item FROM ` + table + ` WHERE partition_key = ANY(` + param(b, idsJson) + `::text[]) AND ` + QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause;
169
168
  return (await PgDriver$ReventlessPostgres.query(P.pool, sql, b.params)).map(extra => decodeRow(true, extra));
170
169
  };
171
170
  let idExprC = `COALESCE(item->>'id', partition_key) COLLATE "C"`;
172
171
  let jsonTextC = field => col(field) + ` COLLATE "C"`;
173
- let listPage = async (readModelName, argsDict, capability, param$1) => {
174
- let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
172
+ let notRetiredC = scope => {
173
+ let key = scope.field.replaceAll("'", "''");
174
+ let states = scope.values;
175
+ if (states !== undefined) {
176
+ if (states.length !== 0) {
177
+ return "(" + states.map(state => {
178
+ let lit = `'"` + state.replaceAll("'", "''") + `"'::jsonb`;
179
+ return `(item->'` + key + `') IS DISTINCT FROM ` + lit;
180
+ }).join(" AND ") + ")";
181
+ } else {
182
+ return "TRUE";
183
+ }
184
+ } else {
185
+ return `(item->'` + key + `') IS DISTINCT FROM ` + "'true'::jsonb";
186
+ }
187
+ };
188
+ let listPage = async (readModelName, argsDict, capability, param$1, ownerScope, retiredScope) => {
189
+ let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
175
190
  let filterDict = Stdlib_Option.getOr(Stdlib_Option.flatMap(argsDict["filter"], Stdlib_JSON.Decode.object), {});
176
191
  let strNonEmpty = k => Stdlib_Option.mapOr(Stdlib_Option.flatMap(filterDict[k], Stdlib_JSON.Decode.string), false, s => s.length > 0);
177
192
  let hasIds = Stdlib_Option.mapOr(Stdlib_Option.flatMap(filterDict["ids"], Stdlib_JSON.Decode.array), false, a => a.length !== 0);
@@ -183,7 +198,13 @@ function Make(P) {
183
198
  let b = {
184
199
  params: []
185
200
  };
186
- let whereParts = [QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause];
201
+ let whereParts = [QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause];
202
+ if (ownerScope !== undefined) {
203
+ whereParts.push(jsonTextC(ownerScope[0]) + ` = ` + param(b, ownerScope[1]));
204
+ }
205
+ if (retiredScope !== undefined) {
206
+ whereParts.push(notRetiredC(retiredScope));
207
+ }
187
208
  let valString = v => {
188
209
  let s = Stdlib_JSON.Decode.string(v);
189
210
  if (s !== undefined) {
@@ -232,8 +253,8 @@ function Make(P) {
232
253
  let cursorValueOf = item => Stdlib_Option.getOr(QueryDbListQuery$ReventlessCore.getFieldString(item, cursorField), QueryDbListQuery$ReventlessCore.getId(item));
233
254
  return QueryDbListQuery$ReventlessCore.buildConnection(pageItems, hasMore, false, cursorValueOf);
234
255
  };
235
- let itemsPage = async (readModelName, param$1, id, argsDict) => {
236
- let table = QueryDbStorage_Postgres$ReventlessPostgres.tableName(readModelName);
256
+ let itemsPage = async (readModelName, param$1, id, argsDict, ownerScope, retiredScope) => {
257
+ let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
237
258
  let filterDict = Stdlib_Option.getOr(Stdlib_Option.flatMap(argsDict["filter"], Stdlib_JSON.Decode.object), {});
238
259
  let fstr = k => Stdlib_Option.flatMap(filterDict[k], Stdlib_JSON.Decode.string);
239
260
  let orderDesc = Stdlib_Option.mapOr(fstr("order"), false, o => o === "DESC");
@@ -248,8 +269,14 @@ function Make(P) {
248
269
  };
249
270
  let where = [
250
271
  `partition_key = ` + param(b, id),
251
- QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause
272
+ QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause
252
273
  ];
274
+ if (ownerScope !== undefined) {
275
+ where.push(jsonTextC(ownerScope[0]) + ` = ` + param(b, ownerScope[1]));
276
+ }
277
+ if (retiredScope !== undefined) {
278
+ where.push(notRetiredC(retiredScope));
279
+ }
253
280
  Stdlib_Option.forEach(fstr("prefix"), p => {
254
281
  where.push(`starts_with(sub_key, ` + param(b, p) + `)`);
255
282
  });
@@ -304,10 +331,6 @@ function Make(P) {
304
331
  }
305
332
  };
306
333
  };
307
- let make = _allQueryDbs => Pulumi.output({
308
- scan: scan,
309
- query: query
310
- });
311
334
  return {
312
335
  query: query,
313
336
  scan: scan,
@@ -315,13 +338,13 @@ function Make(P) {
315
338
  byIds: byIds,
316
339
  idExprC: idExprC,
317
340
  jsonTextC: jsonTextC,
341
+ notRetiredC: notRetiredC,
318
342
  listPage: listPage,
319
- itemsPage: itemsPage,
320
- make: make
343
+ itemsPage: itemsPage
321
344
  };
322
345
  }
323
346
 
324
- let notExpired = QueryDbStorage_Postgres$ReventlessPostgres.notExpiredClause;
347
+ let notExpired = QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause;
325
348
 
326
349
  export {
327
350
  valueToText,
@@ -334,4 +357,4 @@ export {
334
357
  decodeRow,
335
358
  Make,
336
359
  }
337
- /* @pulumi/pulumi Not a pure module */
360
+ /* PgDriver-ReventlessPostgres Not a pure module */
@@ -122,7 +122,7 @@ function orderBy(f, dir) {
122
122
  }
123
123
 
124
124
  function expectedFor(argsDict) {
125
- return norm(QueryDbListQuery$ReventlessCore.run(seededItems, argsDict, capability, "name", param => {}));
125
+ return norm(QueryDbListQuery$ReventlessCore.run(seededItems, argsDict, capability, "name", param => {}, undefined, undefined));
126
126
  }
127
127
 
128
128
  let url = process.env["PG_URL"];
@@ -148,7 +148,7 @@ if (url !== undefined) {
148
148
  });
149
149
  let checkPushed = async (label, args) => {
150
150
  let argsDict = Object.fromEntries(args);
151
- let actual = await Eng.listPage(name, argsDict, capability, "name");
151
+ let actual = await Eng.listPage(name, argsDict, capability, "name", undefined, undefined);
152
152
  if (actual !== undefined) {
153
153
  globalThis.expect(norm(actual)).toEqual(expectedFor(argsDict));
154
154
  } else {
@@ -156,7 +156,7 @@ if (url !== undefined) {
156
156
  }
157
157
  };
158
158
  let checkFallback = async args => {
159
- let r = await Eng.listPage(name, Object.fromEntries(args), capability, "name");
159
+ let r = await Eng.listPage(name, Object.fromEntries(args), capability, "name", undefined, undefined);
160
160
  globalThis.expect(Stdlib_Option.isSome(r)).toBe(false);
161
161
  };
162
162
  globalThis.describe("QueryDb list push-down parity (Postgres ≡ QueryDbListQuery spec)", () => {
@@ -322,7 +322,7 @@ if (url !== undefined) {
322
322
  });
323
323
  let itemSeqs = conn => Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(field(conn, "edges"), Stdlib_JSON.Decode.array), []), e => Stdlib_Option.flatMap(field(e, "node"), n => str(n, "seq")));
324
324
  let pageInfoBool = (conn, key) => Stdlib_Option.getOr(Stdlib_Option.flatMap(field(Stdlib_Option.getOr(field(conn, "pageInfo"), null), key), Stdlib_JSON.Decode.bool), false);
325
- let itemsPage = args => Eng.itemsPage(itemsName, "seq", "order-1", Object.fromEntries(args));
325
+ let itemsPage = args => Eng.itemsPage(itemsName, "seq", "order-1", Object.fromEntries(args), undefined, undefined);
326
326
  globalThis.describe("QueryEnginePostgres itemsPage (sub-id keyset)", () => {
327
327
  globalThis.test("bare → all lines ASC, no next page", async () => {
328
328
  let r = await itemsPage([]);
@@ -1,2 +1,2 @@
1
- #Start(1783542690864)
2
- #Done(1783542692822)
1
+ #Start(1786876628952)
2
+ #Done(1786876634130)
Binary file
@@ -15,8 +15,11 @@
15
15
  // later commit) is never skipped by a checkpointing reader. `global_seq` alone,
16
16
  // being an IDENTITY assigned at INSERT, could NOT provide that guarantee.
17
17
 
18
- // Reuse the shared `<a>:<b>` cursor codec from the DCB storage — identical format.
19
- module Dcb = DcbEventLogStorage_Postgres
18
+ // Reuse the shared `<a>:<b>` cursor codec from the DCB storage — identical
19
+ // format. Point at the runtime-pure `_Ops` module (no `@pulumi/pulumi`): this
20
+ // feed is drained by the deployed change-feed relay Lambda, whose cold-start
21
+ // graph must stay Pulumi-free.
22
+ module Dcb = DcbEventLogStorage_Postgres_Ops
20
23
 
21
24
  // One classic event as the feed surfaces it. `payload` is the stored event JSON
22
25
  // verbatim; a bus/topic relay transforms it into its target shape (e.g. the AWS
Binary file
@@ -18,7 +18,12 @@
18
18
  open ReventlessCore
19
19
  open Reventless
20
20
 
21
- module Dcb = DcbEventLogStorage_Postgres
21
+ // Runtime-pure ops module (no `@pulumi/pulumi`) — this feed is drained by the
22
+ // deployed change-feed relay Lambda, so its cold-start graph must stay
23
+ // Pulumi-free. `_Ops` carries every helper used below (mkBuilder,
24
+ // buildReadWhere, decodeCursor, selectColumns, rowToEvent); the deploy-time
25
+ // DcbEventLogStorage_Postgres only `include`s it and adds the Pulumi adapter.
26
+ module Dcb = DcbEventLogStorage_Postgres_Ops
22
27
 
23
28
  // One page of the feed. `events` are in (transaction_id, position) order;
24
29
  // `cursor` is the checkpoint to persist and to pass as the next `after` (None
Binary file
Binary file
Binary file
@@ -37,6 +37,6 @@ module Make = (P: {let pool: PgDriver.pool}) => {
37
37
  ~ttl as _=?,
38
38
  ~api as _,
39
39
  ~apiRole as _,
40
- ~opts as _,
40
+ ~owner as _, ~opts as _,
41
41
  ) => makeStorage(~pool=P.pool, ~name, ~indexes, ~subIdField)
42
42
  }
Binary file
@@ -63,18 +63,18 @@ let subIdSql = (b: paramBuilder, field: string, cmp: QueryEngine.SubId.comparato
63
63
  }
64
64
  }
65
65
 
66
- let notExpired = QueryDbStorage_Postgres.notExpiredClause
66
+ let notExpired = QueryDbStorage_Postgres_Ops.notExpiredClause
67
67
 
68
68
  // Add `id` (from partition_key) when absent — matches allRows/scan in the SQLite
69
69
  // engine; partition-key queries return items as stored.
70
70
  let decodeRow = (~withId, row: dict<JSON.t>): JSON.t => {
71
- let item = QueryDbStorage_Postgres.decodeItem(row)
71
+ let item = QueryDbStorage_Postgres_Ops.decodeItem(row)
72
72
  if withId {
73
73
  let pk = switch row->Dict.get("partition_key") {
74
74
  | Some(JSON.String(s)) => s
75
75
  | _ => ""
76
76
  }
77
- QueryDbStorage_Postgres.withId(item, pk)
77
+ QueryDbStorage_Postgres_Ops.withId(item, pk)
78
78
  } else {
79
79
  item
80
80
  }
@@ -90,7 +90,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
90
90
  ~ascending=?,
91
91
  ~limit=?,
92
92
  ): array<JSON.t> => {
93
- let table = QueryDbStorage_Postgres.tableName(readModelName)
93
+ let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
94
94
  let keyStr = switch key {
95
95
  | Some(k) => k
96
96
  | None => valueToText(id)
@@ -114,7 +114,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
114
114
  }
115
115
 
116
116
  let scan = async (~readModelName, ~filterConfigs, ~limit): array<JSON.t> => {
117
- let table = QueryDbStorage_Postgres.tableName(readModelName)
117
+ let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
118
118
  let b = {params: []}
119
119
  let where = [notExpired]
120
120
  filterConfigs->Array.forEach(((field, cmp, v)) => where->Array.push(filterSql(b, field, cmp, v)))
@@ -138,7 +138,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
138
138
  // (`ensureIndexes`) instead of scanning. String comparison mirrors the
139
139
  // resolver's JS path (which only matched string-typed fields).
140
140
  let indexLookup = async (~readModelName, field: string, value: string): array<JSON.t> => {
141
- let table = QueryDbStorage_Postgres.tableName(readModelName)
141
+ let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
142
142
  let b = {params: []}
143
143
  let sql = `SELECT partition_key, item FROM ${table} WHERE ${col(field)} = ${b->param(
144
144
  JSON.Encode.string(value),
@@ -154,7 +154,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
154
154
  if ids->Array.length == 0 {
155
155
  []
156
156
  } else {
157
- let table = QueryDbStorage_Postgres.tableName(readModelName)
157
+ let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
158
158
  let b = {params: []}
159
159
  let idsJson = JSON.Encode.array(ids->Array.map(JSON.Encode.string))
160
160
  let sql = `SELECT partition_key, item FROM ${table} WHERE partition_key = ANY(${b->param(
@@ -168,6 +168,39 @@ module Make = (P: {let pool: PgDriver.pool}) => {
168
168
  let idExprC = `COALESCE(item->>'id', partition_key) COLLATE "C"`
169
169
  let jsonTextC = (field: string): string => `${col(field)} COLLATE "C"`
170
170
 
171
+ // "not retired", as jsonb rather than text. `->` keeps the value typed, so a
172
+ // JSON boolean true is distinguishable from the JSON string "true"; `->>`
173
+ // would render both as 'true' and hide a row the shared spec keeps, since that
174
+ // spec decodes strictly and reads a non-boolean as "no flag". `IS DISTINCT
175
+ // FROM` rather than `<>` so an absent key — NULL — keeps the row instead of
176
+ // making the whole predicate NULL and dropping it.
177
+ //
178
+ // The state form compares against JSON strings instead of `true`, one
179
+ // `IS DISTINCT FROM` per state, ANDed. Not `NOT IN`: that is ordinary
180
+ // equality, so an absent key makes the predicate NULL and drops a row this
181
+ // spec keeps. Same shape and same index behaviour as the single-state form —
182
+ // a handful of equality predicates over an enum-valued attribute cost what one
183
+ // over a boolean does — so the `@index` guidance carries over unchanged.
184
+ //
185
+ // A set naming no states withdraws nothing, so the predicate is vacuously
186
+ // true, matching what `isRetiredValue` answers for it.
187
+ let notRetiredC = (scope: Reventless.OwnerScope.retiredScope): string => {
188
+ let key = scope.field->String.replaceAll("'", "''")
189
+ let distinctFrom = lit => `(item->'${key}') IS DISTINCT FROM ${lit}`
190
+ switch scope.values {
191
+ | None => distinctFrom("'true'::jsonb")
192
+ | Some([]) => "TRUE"
193
+ | Some(states) =>
194
+ // Parenthesised though every caller ANDs these together already: a clause
195
+ // that is safe only because of where it is spliced is a clause that breaks
196
+ // the first time it is spliced somewhere else.
197
+ "(" ++
198
+ states
199
+ ->Array.map(state => distinctFrom(`'"${state->String.replaceAll("'", "''")}"'::jsonb`))
200
+ ->Array.join(" AND ") ++ ")"
201
+ }
202
+ }
203
+
171
204
  // Connection-list push-down. Builds `item->>'field'` predicates + keyset
172
205
  // WHERE + `ORDER BY … LIMIT pageSize+1` so a page reads only what it returns
173
206
  // rather than the whole read model. Reproduces `QueryDbListQuery` semantics
@@ -183,8 +216,10 @@ module Make = (P: {let pool: PgDriver.pool}) => {
183
216
  ~argsDict: dict<JSON.t>,
184
217
  ~capability: GraphQL_FragmentGenerator.serverCapability,
185
218
  ~labelField as _,
219
+ ~ownerScope: option<(string, string)>=?,
220
+ ~retiredScope: option<Reventless.OwnerScope.retiredScope>=?,
186
221
  ): option<JSON.t> => {
187
- let table = QueryDbStorage_Postgres.tableName(readModelName)
222
+ let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
188
223
  let filterDict =
189
224
  argsDict->Dict.get("filter")->Option.flatMap(JSON.Decode.object)->Option.getOr(Dict.make())
190
225
  let strNonEmpty = k =>
@@ -210,6 +245,19 @@ module Make = (P: {let pool: PgDriver.pool}) => {
210
245
  } else {
211
246
  let b = {params: []}
212
247
  let whereParts = [notExpired]
248
+ // Into the SQL, before the LIMIT — narrowing the returned page instead
249
+ // would hand back fewer rows than asked for while still claiming a next
250
+ // page. Mirrors the SQLite push-down; the parity harness holds the two
251
+ // and the shared spec to the same answer.
252
+ switch ownerScope {
253
+ | Some((field, required)) =>
254
+ whereParts->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
255
+ | None => ()
256
+ }
257
+ switch retiredScope {
258
+ | Some(scope) => whereParts->Array.push(notRetiredC(scope))
259
+ | None => ()
260
+ }
213
261
  let valString = v =>
214
262
  switch v->JSON.Decode.string {
215
263
  | Some(s) => Some(s)
@@ -309,8 +357,10 @@ module Make = (P: {let pool: PgDriver.pool}) => {
309
357
  ~subIdField as _: string,
310
358
  ~id: string,
311
359
  ~argsDict: dict<JSON.t>,
360
+ ~ownerScope: option<(string, string)>=?,
361
+ ~retiredScope: option<Reventless.OwnerScope.retiredScope>=?,
312
362
  ): JSON.t => {
313
- let table = QueryDbStorage_Postgres.tableName(readModelName)
363
+ let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
314
364
  let filterDict =
315
365
  argsDict->Dict.get("filter")->Option.flatMap(JSON.Decode.object)->Option.getOr(Dict.make())
316
366
  let fstr = k => filterDict->Dict.get(k)->Option.flatMap(JSON.Decode.string)
@@ -324,6 +374,16 @@ module Make = (P: {let pool: PgDriver.pool}) => {
324
374
  let sk = `sub_key COLLATE "C"`
325
375
  let b = {params: []}
326
376
  let where = [`partition_key = ${b->param(JSON.Encode.string(id))}`, notExpired]
377
+ // Before the keyset window and the LIMIT, for the same reason as `listPage`.
378
+ switch ownerScope {
379
+ | Some((field, required)) =>
380
+ where->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
381
+ | None => ()
382
+ }
383
+ switch retiredScope {
384
+ | Some(scope) => where->Array.push(notRetiredC(scope))
385
+ | None => ()
386
+ }
327
387
  fstr("prefix")->Option.forEach(p =>
328
388
  where->Array.push(`starts_with(sub_key, ${b->param(JSON.Encode.string(p))})`)
329
389
  )
@@ -380,7 +440,4 @@ module Make = (P: {let pool: PgDriver.pool}) => {
380
440
  },
381
441
  })
382
442
  }
383
-
384
- let make: QueryDb_Adapter.queryEngineMaker = _allQueryDbs =>
385
- Pulumi.Output.make({QueryEngine.scan, query})
386
443
  }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-postgres",
3
- "version": "3.0.0-alpha.10",
3
+ "version": "3.0.0-alpha.101",
4
4
  "description": "Postgres storage backend for Reventless (classic + DCB event logs, query DB, change feed). Connection-string only — RDS/Aurora, any managed provider, or a local container.",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -22,21 +22,21 @@
22
22
  "dependencies": {
23
23
  "pg": "^8.13.1",
24
24
  "sury": "11.0.0-alpha.4",
25
- "@reventlessdev/rescript-jest": "1.0.0-alpha.6",
26
- "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
27
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
28
- "@reventlessdev/reventless-infra": "3.0.0-alpha.91",
29
- "@reventlessdev/reventless-core": "3.0.0-alpha.146",
30
- "@reventlessdev/reventless-spec": "3.0.0-alpha.69"
25
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
26
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
27
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
28
+ "@reventlessdev/reventless-core": "3.0.0-alpha.237",
29
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.143",
30
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.115"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/pg": "^8.11.10",
34
- "rescript": "^12.3.0",
34
+ "rescript": "12.3.0",
35
35
  "sury-ppx": "11.0.0-alpha.2",
36
- "@reventlessdev/reventless-ppx": "1.0.0-alpha.51"
36
+ "@reventlessdev/reventless-ppx": "1.0.0-alpha.67"
37
37
  },
38
38
  "peerDependencies": {
39
- "rescript": "^12.3.0"
39
+ "rescript": "12.3.0"
40
40
  },
41
41
  "publishConfig": {
42
42
  "registry": "https://registry.npmjs.org"
@@ -44,7 +44,7 @@
44
44
  "repository": {
45
45
  "type": "git",
46
46
  "url": "git+https://github.com/ReventlessDev/reventless-core.git",
47
- "directory": "reventless/reventless-postgres"
47
+ "directory": "reventless/postgres"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "rescript build",
package/rescript.json CHANGED
@@ -19,7 +19,8 @@
19
19
  },
20
20
  {
21
21
  "dir": "tests",
22
- "subdirs": true
22
+ "subdirs": true,
23
+ "type": "dev"
23
24
  }
24
25
  ],
25
26
  "dependencies": [
@@ -15,8 +15,11 @@
15
15
  // later commit) is never skipped by a checkpointing reader. `global_seq` alone,
16
16
  // being an IDENTITY assigned at INSERT, could NOT provide that guarantee.
17
17
 
18
- // Reuse the shared `<a>:<b>` cursor codec from the DCB storage — identical format.
19
- module Dcb = DcbEventLogStorage_Postgres
18
+ // Reuse the shared `<a>:<b>` cursor codec from the DCB storage — identical
19
+ // format. Point at the runtime-pure `_Ops` module (no `@pulumi/pulumi`): this
20
+ // feed is drained by the deployed change-feed relay Lambda, whose cold-start
21
+ // graph must stay Pulumi-free.
22
+ module Dcb = DcbEventLogStorage_Postgres_Ops
20
23
 
21
24
  // One classic event as the feed surfaces it. `payload` is the stored event JSON
22
25
  // verbatim; a bus/topic relay transforms it into its target shape (e.g. the AWS
@@ -3,7 +3,7 @@
3
3
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as PgDriver$ReventlessPostgres from "./PgDriver.res.mjs";
6
- import * as DcbEventLogStorage_Postgres$ReventlessPostgres from "./DcbEventLogStorage_Postgres.res.mjs";
6
+ import * as DcbEventLogStorage_Postgres_Ops$ReventlessPostgres from "./DcbEventLogStorage_Postgres_Ops.res.mjs";
7
7
 
8
8
  let selectColumns = "\n lpad(transaction_id::text, 20, '0') || ':' || lpad(global_seq::text, 20, '0') AS cursor,\n aggregate_id,\n seq_nr,\n payload,\n msg_id\n";
9
9
 
@@ -43,7 +43,7 @@ async function readBatch(pool, logName, after, limitOpt) {
43
43
  let limit = limitOpt !== undefined ? limitOpt : 500;
44
44
  let params = [logName];
45
45
  let where = "log_name = $1 AND transaction_id < pg_snapshot_xmin(pg_current_snapshot())";
46
- let match = Stdlib_Option.flatMap(after, DcbEventLogStorage_Postgres$ReventlessPostgres.decodeCursor);
46
+ let match = Stdlib_Option.flatMap(after, DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.decodeCursor);
47
47
  if (match !== undefined) {
48
48
  params.push(match[0]);
49
49
  params.push(match[1]);
@@ -72,7 +72,7 @@ async function loadCheckpoint(pool, subscriber) {
72
72
  }
73
73
 
74
74
  async function saveCheckpoint(pool, subscriber, cursor) {
75
- let match = DcbEventLogStorage_Postgres$ReventlessPostgres.decodeCursor(cursor);
75
+ let match = DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.decodeCursor(cursor);
76
76
  if (match !== undefined) {
77
77
  await PgDriver$ReventlessPostgres.query(pool, "INSERT INTO event_log_subscription(subscriber, last_tx, last_global_seq)\n VALUES ($1, $2::xid8, $3::bigint)\n ON CONFLICT (subscriber)\n DO UPDATE SET last_tx = EXCLUDED.last_tx, last_global_seq = EXCLUDED.last_global_seq", [
78
78
  subscriber,