@reventlessdev/reventless-postgres 3.0.0-alpha.87 → 3.0.0-alpha.89

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 (53) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/lib/bs/.compiler.log +2 -2
  3. package/lib/bs/compiler-info.json +1 -1
  4. package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  5. package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  6. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  7. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  8. package/lib/bs/src/EventLogChangeFeed-ReventlessPostgres.cmi +0 -0
  9. package/lib/bs/src/EventLogChangeFeed-ReventlessPostgres.cmt +0 -0
  10. package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  11. package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  12. package/lib/bs/src/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  13. package/lib/bs/src/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  14. package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmi +0 -0
  15. package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmt +0 -0
  16. package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
  17. package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
  18. package/lib/bs/src/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  19. package/lib/bs/src/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  20. package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
  21. package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
  22. package/lib/bs/src/QueryEnginePostgres.ast +0 -0
  23. package/lib/bs/src/QueryEnginePostgres.res +17 -0
  24. package/lib/bs/src/QueryEnginePostgres.res.mjs +8 -2
  25. package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmi +0 -0
  26. package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmt +0 -0
  27. package/lib/bs/tests/QueryEnginePostgresListPageTest-ReventlessPostgres.cmi +0 -0
  28. package/lib/bs/tests/QueryEnginePostgresListPageTest-ReventlessPostgres.cmt +0 -0
  29. package/lib/bs/tests/QueryEnginePostgresListPageTest.res.mjs +4 -4
  30. package/lib/ocaml/.compiler.log +2 -2
  31. package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  32. package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  33. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  34. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  35. package/lib/ocaml/EventLogChangeFeed-ReventlessPostgres.cmi +0 -0
  36. package/lib/ocaml/EventLogChangeFeed-ReventlessPostgres.cmt +0 -0
  37. package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  38. package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  39. package/lib/ocaml/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  40. package/lib/ocaml/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  41. package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmi +0 -0
  42. package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmt +0 -0
  43. package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
  44. package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
  45. package/lib/ocaml/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  46. package/lib/ocaml/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  47. package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
  48. package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
  49. package/lib/ocaml/QueryEnginePostgres.ast +0 -0
  50. package/lib/ocaml/QueryEnginePostgres.res +17 -0
  51. package/package.json +4 -4
  52. package/src/QueryEnginePostgres.res +17 -0
  53. package/src/QueryEnginePostgres.res.mjs +8 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.89 (2026-08-12)
7
+
8
+ ### Features
9
+
10
+ * **queries:** narrow owner-bearing reads to the caller ([ba9cc3d](https://github.com/ReventlessDev/reventless-core/commit/ba9cc3d58d7914a9e4827bda90a704a74b1b82dd))
11
+
12
+
13
+ # 3.0.0-alpha.88 (2026-08-11)
14
+
15
+ **Note:** Version bump only for package @reventlessdev/reventless-postgres
16
+
17
+
18
+
19
+
20
+
6
21
  # 3.0.0-alpha.87 (2026-08-10)
7
22
 
8
23
  **Note:** Version bump only for package @reventlessdev/reventless-postgres
@@ -1,2 +1,2 @@
1
- #Start(1786387837770)
2
- #Done(1786387843584)
1
+ #Start(1786519558375)
2
+ #Done(1786519562738)
@@ -4,5 +4,5 @@
4
4
  "bsc_hash": "75e3a59c95cc953608fd3dd6ea6ed68141bba4414a20a52f114261b8a48385fa",
5
5
  "rescript_config_hash": "af2af60a0fa33b8249a96ff2758f36482843126dc4e07b9302842d011e0ad8c6",
6
6
  "runtime_path": "/home/runner/work/reventless-core/reventless-core/node_modules/@rescript/runtime",
7
- "generated_at": "1786387843599"
7
+ "generated_at": "1786519562743"
8
8
  }
Binary file
@@ -183,6 +183,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
183
183
  ~argsDict: dict<JSON.t>,
184
184
  ~capability: GraphQL_FragmentGenerator.serverCapability,
185
185
  ~labelField as _,
186
+ ~ownerScope: option<(string, string)>=?,
186
187
  ): option<JSON.t> => {
187
188
  let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
188
189
  let filterDict =
@@ -210,6 +211,15 @@ module Make = (P: {let pool: PgDriver.pool}) => {
210
211
  } else {
211
212
  let b = {params: []}
212
213
  let whereParts = [notExpired]
214
+ // Into the SQL, before the LIMIT — narrowing the returned page instead
215
+ // would hand back fewer rows than asked for while still claiming a next
216
+ // page. Mirrors the SQLite push-down; the parity harness holds the two
217
+ // and the shared spec to the same answer.
218
+ switch ownerScope {
219
+ | Some((field, required)) =>
220
+ whereParts->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
221
+ | None => ()
222
+ }
213
223
  let valString = v =>
214
224
  switch v->JSON.Decode.string {
215
225
  | Some(s) => Some(s)
@@ -309,6 +319,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
309
319
  ~subIdField as _: string,
310
320
  ~id: string,
311
321
  ~argsDict: dict<JSON.t>,
322
+ ~ownerScope: option<(string, string)>=?,
312
323
  ): JSON.t => {
313
324
  let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
314
325
  let filterDict =
@@ -324,6 +335,12 @@ module Make = (P: {let pool: PgDriver.pool}) => {
324
335
  let sk = `sub_key COLLATE "C"`
325
336
  let b = {params: []}
326
337
  let where = [`partition_key = ${b->param(JSON.Encode.string(id))}`, notExpired]
338
+ // Before the keyset window and the LIMIT, for the same reason as `listPage`.
339
+ switch ownerScope {
340
+ | Some((field, required)) =>
341
+ where->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
342
+ | None => ()
343
+ }
327
344
  fstr("prefix")->Option.forEach(p =>
328
345
  where->Array.push(`starts_with(sub_key, ${b->param(JSON.Encode.string(p))})`)
329
346
  )
@@ -169,7 +169,7 @@ function Make(P) {
169
169
  };
170
170
  let idExprC = `COALESCE(item->>'id', partition_key) COLLATE "C"`;
171
171
  let jsonTextC = field => col(field) + ` COLLATE "C"`;
172
- let listPage = async (readModelName, argsDict, capability, param$1) => {
172
+ let listPage = async (readModelName, argsDict, capability, param$1, ownerScope) => {
173
173
  let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
174
174
  let filterDict = Stdlib_Option.getOr(Stdlib_Option.flatMap(argsDict["filter"], Stdlib_JSON.Decode.object), {});
175
175
  let strNonEmpty = k => Stdlib_Option.mapOr(Stdlib_Option.flatMap(filterDict[k], Stdlib_JSON.Decode.string), false, s => s.length > 0);
@@ -183,6 +183,9 @@ function Make(P) {
183
183
  params: []
184
184
  };
185
185
  let whereParts = [QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause];
186
+ if (ownerScope !== undefined) {
187
+ whereParts.push(jsonTextC(ownerScope[0]) + ` = ` + param(b, ownerScope[1]));
188
+ }
186
189
  let valString = v => {
187
190
  let s = Stdlib_JSON.Decode.string(v);
188
191
  if (s !== undefined) {
@@ -231,7 +234,7 @@ function Make(P) {
231
234
  let cursorValueOf = item => Stdlib_Option.getOr(QueryDbListQuery$ReventlessCore.getFieldString(item, cursorField), QueryDbListQuery$ReventlessCore.getId(item));
232
235
  return QueryDbListQuery$ReventlessCore.buildConnection(pageItems, hasMore, false, cursorValueOf);
233
236
  };
234
- let itemsPage = async (readModelName, param$1, id, argsDict) => {
237
+ let itemsPage = async (readModelName, param$1, id, argsDict, ownerScope) => {
235
238
  let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
236
239
  let filterDict = Stdlib_Option.getOr(Stdlib_Option.flatMap(argsDict["filter"], Stdlib_JSON.Decode.object), {});
237
240
  let fstr = k => Stdlib_Option.flatMap(filterDict[k], Stdlib_JSON.Decode.string);
@@ -249,6 +252,9 @@ function Make(P) {
249
252
  `partition_key = ` + param(b, id),
250
253
  QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause
251
254
  ];
255
+ if (ownerScope !== undefined) {
256
+ where.push(jsonTextC(ownerScope[0]) + ` = ` + param(b, ownerScope[1]));
257
+ }
252
258
  Stdlib_Option.forEach(fstr("prefix"), p => {
253
259
  where.push(`starts_with(sub_key, ` + param(b, p) + `)`);
254
260
  });
@@ -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));
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);
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);
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);
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(1786387837770)
2
- #Done(1786387843584)
1
+ #Start(1786519558375)
2
+ #Done(1786519562738)
Binary file
@@ -183,6 +183,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
183
183
  ~argsDict: dict<JSON.t>,
184
184
  ~capability: GraphQL_FragmentGenerator.serverCapability,
185
185
  ~labelField as _,
186
+ ~ownerScope: option<(string, string)>=?,
186
187
  ): option<JSON.t> => {
187
188
  let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
188
189
  let filterDict =
@@ -210,6 +211,15 @@ module Make = (P: {let pool: PgDriver.pool}) => {
210
211
  } else {
211
212
  let b = {params: []}
212
213
  let whereParts = [notExpired]
214
+ // Into the SQL, before the LIMIT — narrowing the returned page instead
215
+ // would hand back fewer rows than asked for while still claiming a next
216
+ // page. Mirrors the SQLite push-down; the parity harness holds the two
217
+ // and the shared spec to the same answer.
218
+ switch ownerScope {
219
+ | Some((field, required)) =>
220
+ whereParts->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
221
+ | None => ()
222
+ }
213
223
  let valString = v =>
214
224
  switch v->JSON.Decode.string {
215
225
  | Some(s) => Some(s)
@@ -309,6 +319,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
309
319
  ~subIdField as _: string,
310
320
  ~id: string,
311
321
  ~argsDict: dict<JSON.t>,
322
+ ~ownerScope: option<(string, string)>=?,
312
323
  ): JSON.t => {
313
324
  let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
314
325
  let filterDict =
@@ -324,6 +335,12 @@ module Make = (P: {let pool: PgDriver.pool}) => {
324
335
  let sk = `sub_key COLLATE "C"`
325
336
  let b = {params: []}
326
337
  let where = [`partition_key = ${b->param(JSON.Encode.string(id))}`, notExpired]
338
+ // Before the keyset window and the LIMIT, for the same reason as `listPage`.
339
+ switch ownerScope {
340
+ | Some((field, required)) =>
341
+ where->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
342
+ | None => ()
343
+ }
327
344
  fstr("prefix")->Option.forEach(p =>
328
345
  where->Array.push(`starts_with(sub_key, ${b->param(JSON.Encode.string(p))})`)
329
346
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-postgres",
3
- "version": "3.0.0-alpha.87",
3
+ "version": "3.0.0-alpha.89",
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": {
@@ -25,9 +25,9 @@
25
25
  "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
26
26
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
27
27
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
28
- "@reventlessdev/reventless-core": "3.0.0-alpha.223",
29
- "@reventlessdev/reventless-infra": "3.0.0-alpha.133",
30
- "@reventlessdev/reventless-spec": "3.0.0-alpha.107"
28
+ "@reventlessdev/reventless-core": "3.0.0-alpha.225",
29
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.135",
30
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.109"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/pg": "^8.11.10",
@@ -183,6 +183,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
183
183
  ~argsDict: dict<JSON.t>,
184
184
  ~capability: GraphQL_FragmentGenerator.serverCapability,
185
185
  ~labelField as _,
186
+ ~ownerScope: option<(string, string)>=?,
186
187
  ): option<JSON.t> => {
187
188
  let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
188
189
  let filterDict =
@@ -210,6 +211,15 @@ module Make = (P: {let pool: PgDriver.pool}) => {
210
211
  } else {
211
212
  let b = {params: []}
212
213
  let whereParts = [notExpired]
214
+ // Into the SQL, before the LIMIT — narrowing the returned page instead
215
+ // would hand back fewer rows than asked for while still claiming a next
216
+ // page. Mirrors the SQLite push-down; the parity harness holds the two
217
+ // and the shared spec to the same answer.
218
+ switch ownerScope {
219
+ | Some((field, required)) =>
220
+ whereParts->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
221
+ | None => ()
222
+ }
213
223
  let valString = v =>
214
224
  switch v->JSON.Decode.string {
215
225
  | Some(s) => Some(s)
@@ -309,6 +319,7 @@ module Make = (P: {let pool: PgDriver.pool}) => {
309
319
  ~subIdField as _: string,
310
320
  ~id: string,
311
321
  ~argsDict: dict<JSON.t>,
322
+ ~ownerScope: option<(string, string)>=?,
312
323
  ): JSON.t => {
313
324
  let table = QueryDbStorage_Postgres_Ops.tableName(readModelName)
314
325
  let filterDict =
@@ -324,6 +335,12 @@ module Make = (P: {let pool: PgDriver.pool}) => {
324
335
  let sk = `sub_key COLLATE "C"`
325
336
  let b = {params: []}
326
337
  let where = [`partition_key = ${b->param(JSON.Encode.string(id))}`, notExpired]
338
+ // Before the keyset window and the LIMIT, for the same reason as `listPage`.
339
+ switch ownerScope {
340
+ | Some((field, required)) =>
341
+ where->Array.push(`${jsonTextC(field)} = ${b->param(JSON.Encode.string(required))}`)
342
+ | None => ()
343
+ }
327
344
  fstr("prefix")->Option.forEach(p =>
328
345
  where->Array.push(`starts_with(sub_key, ${b->param(JSON.Encode.string(p))})`)
329
346
  )
@@ -169,7 +169,7 @@ function Make(P) {
169
169
  };
170
170
  let idExprC = `COALESCE(item->>'id', partition_key) COLLATE "C"`;
171
171
  let jsonTextC = field => col(field) + ` COLLATE "C"`;
172
- let listPage = async (readModelName, argsDict, capability, param$1) => {
172
+ let listPage = async (readModelName, argsDict, capability, param$1, ownerScope) => {
173
173
  let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
174
174
  let filterDict = Stdlib_Option.getOr(Stdlib_Option.flatMap(argsDict["filter"], Stdlib_JSON.Decode.object), {});
175
175
  let strNonEmpty = k => Stdlib_Option.mapOr(Stdlib_Option.flatMap(filterDict[k], Stdlib_JSON.Decode.string), false, s => s.length > 0);
@@ -183,6 +183,9 @@ function Make(P) {
183
183
  params: []
184
184
  };
185
185
  let whereParts = [QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause];
186
+ if (ownerScope !== undefined) {
187
+ whereParts.push(jsonTextC(ownerScope[0]) + ` = ` + param(b, ownerScope[1]));
188
+ }
186
189
  let valString = v => {
187
190
  let s = Stdlib_JSON.Decode.string(v);
188
191
  if (s !== undefined) {
@@ -231,7 +234,7 @@ function Make(P) {
231
234
  let cursorValueOf = item => Stdlib_Option.getOr(QueryDbListQuery$ReventlessCore.getFieldString(item, cursorField), QueryDbListQuery$ReventlessCore.getId(item));
232
235
  return QueryDbListQuery$ReventlessCore.buildConnection(pageItems, hasMore, false, cursorValueOf);
233
236
  };
234
- let itemsPage = async (readModelName, param$1, id, argsDict) => {
237
+ let itemsPage = async (readModelName, param$1, id, argsDict, ownerScope) => {
235
238
  let table = QueryDbStorage_Postgres_Ops$ReventlessPostgres.tableName(readModelName);
236
239
  let filterDict = Stdlib_Option.getOr(Stdlib_Option.flatMap(argsDict["filter"], Stdlib_JSON.Decode.object), {});
237
240
  let fstr = k => Stdlib_Option.flatMap(filterDict[k], Stdlib_JSON.Decode.string);
@@ -249,6 +252,9 @@ function Make(P) {
249
252
  `partition_key = ` + param(b, id),
250
253
  QueryDbStorage_Postgres_Ops$ReventlessPostgres.notExpiredClause
251
254
  ];
255
+ if (ownerScope !== undefined) {
256
+ where.push(jsonTextC(ownerScope[0]) + ` = ` + param(b, ownerScope[1]));
257
+ }
252
258
  Stdlib_Option.forEach(fstr("prefix"), p => {
253
259
  where.push(`starts_with(sub_key, ` + param(b, p) + `)`);
254
260
  });