@simtlix/simfinity-postgres 3.15.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,7 +31,7 @@ import { postgresPlugin } from '@simtlix/simfinity-postgres';
31
31
  const simfinity = createSQL({ plugin: postgresPlugin({ pool, schema: 'library' }) });
32
32
  ```
33
33
 
34
- Install `@simtlix/simfinity-sql@3.15.0` as a direct dependency when importing its factory. PostgreSQL 15 or later is the only supported SQL plugin. The [SQL package guide](https://github.com/simtlix/simfinity.js/tree/master/packages/sql) documents relational planning and the versioned plugin contract.
34
+ Install `@simtlix/simfinity-sql@3.16.0` as a direct dependency when importing its factory. PostgreSQL 15 or later is the only supported SQL plugin. The [SQL package guide](https://github.com/simtlix/simfinity.js/tree/master/packages/sql) documents relational planning and the versioned plugin contract.
35
35
 
36
36
  Both factories permanently bind one pool and schema, and five optional settings: `multiRowInserts: false` keeps one `INSERT` per owned row instead of multi-row statements; `explicitNullsOrder: true` keeps the `NULLS` clause on sorts of `id` and required fields, as 3.5.9 did, instead of letting indexes serve them; `batchedCollections: true` reads the generated collection fields of the parents that share their arguments with one statement per 100 parents, in a transaction that turns JIT off for that transaction only, instead of one statement per field as 3.6.0 did and as the default still does, and can return unsorted and tied rows in another order; the opt-in `refreshTriggers: 'perRoot'` refreshes each changed root's owned shape and unique keys once per transaction, while its guard trigger stays deferred, instead of once per changed row; and the opt-in `ownerIndexes: 'minimal'` leaves out the `(__owner_id)` indexes that the primary key or a unique index covers on new owned and guard tables. An unknown `refreshTriggers` or `ownerIndexes` value throws `INVALID_DATABASE_SCHEMA` (400). Read the [upgrade notes](https://simtlix.github.io/simfinity.js/resources/compatibility.html#upgrade-to-3-6-0) before enabling the opt-ins: versions up to 3.5.9 cannot validate databases created with them. The returned runtime supports registration, generated queries and mutations, middleware and scopes, controllers and validators, custom queries and mutations, named views, state machines, nested relationship writes, embedded reconstruction, and native PostgreSQL model handles. Native handles expose `findById`, `find`, `create`, `update`, and `delete`; they are not Mongoose models. `find(args, { session })` accepts the generated GraphQL list query arguments. Existing `pg.Pool` configuration and `pg.PoolClient` query interfaces remain compatible across both factories. A custom pool or client wrapper should pass `pg`'s `command` through in query results: the plugin reads it from the `COMMIT` result to detect a transaction that PostgreSQL rolled back because one of its statements failed, and without it such a transaction reports success with nothing stored.
37
37
 
@@ -55,6 +55,8 @@ await initializeDatabase({ mode: 'validate' });
55
55
 
56
56
  Called without arguments after `createSchema()`, the module's `describeDatabase()` and `compileDatabaseSchema()` describe and compile this default instance in its configured schema.
57
57
 
58
+ Since 3.16.0, the module and every instance also expose `updateObject`, `deleteObject` and `transitionObject` next to `saveObject`, which run the update, delete and state action lifecycle in an owned transaction or in a session from `withTransaction`, and `createSchema(…, { transaction })` can add a mutation that runs several generated mutations in one PostgreSQL transaction.
59
+
58
60
  Transactions use one borrowed `pg` client and repeatable-read isolation. Reads without a session of types without owned embedded tables run as a single statement on a pooled connection instead. With `batchedCollections: true`, the runtime reads the generated collection fields of up to 100 parents that share their arguments in one batch, with one statement, a `UNION ALL` of each parent's own statement with its parent ID as a parameter of its own, so PostgreSQL plans each parent's rows as it plans that parent's own statement; more parents run as several batches, each in its own transaction. Each batch runs in one repeatable-read transaction: `BEGIN`, `SET LOCAL jit = off`, the statement, the owned-row reads, then `COMMIT`. Only a direct `findChildrenBatch` call on the SQL adapter with more than 100 IDs repeats `SET LOCAL` and the statement for each further 100 within its transaction. The batch's plan can cost more than `jit_above_cost` when no parent's own plan does, and JIT compilation of its branches can take longer than the statement itself; `SET LOCAL` ends with the transaction, so pooled connections keep the server's setting. A batch takes these 4 round trips, or more with owned rows, one after another on one connection and runs its parents one after another, where per-parent statements take one round trip each, concurrently on the pool's free connections. It pays off when the pool is saturated or much smaller than the number of parents per level, as with many parents with small child lists; with free connections it can be slower, even on a remote database, so measure before enabling it. Transaction-mode PgBouncer keeps each batch on one server connection, while Amazon RDS Proxy pins the pooled connections that run batches, for their `SET LOCAL` and their statements over 16 KB. See [batched collection reads](https://simtlix.github.io/simfinity.js/guide/relationships.html#batched-collection-reads). A supplied Simfinity PostgreSQL session joins the active transaction. Serialization failures and deadlocks are retried up to five times, each after a short random delay that starts once the client is released; mutation input is reset for each attempt. Unknown commit outcomes are not replayed. The caller owns the pool lifecycle.
59
61
 
60
62
  The module and every `createPostgres` instance expose the shared `auth`, `validators`, `scalars`, and `plugins` helpers. These are the same helper objects exported by the MongoDB package, so rules, scalar identities, and errors can be shared safely between backends.
@@ -62,7 +64,7 @@ The module and every `createPostgres` instance expose the shared `auth`, `valida
62
64
  MCP integration is an independent opt-in and does not add MCP dependencies to PostgreSQL applications:
63
65
 
64
66
  ```sh
65
- npm install @simtlix/simfinity-mcp@3.15.0 @modelcontextprotocol/sdk@^1.31.0
67
+ npm install @simtlix/simfinity-mcp@3.16.0 @modelcontextprotocol/sdk@^1.31.0
66
68
  ```
67
69
 
68
70
  ```javascript
@@ -77,7 +79,7 @@ Entity identities and references use UUIDs. Scalars use native PostgreSQL types,
77
79
 
78
80
  Queries support nested scalar/reference paths, logical filter groups, scopes, sort, pagination, count, and grouped `SUM`, `COUNT`, `AVG`, `MIN`, and `MAX` facts. The PostgreSQL implementation preserves root multiplicity when joining collection paths.
79
81
 
80
- The text operators take literal values as bound parameters. `LIKE`, `STARTS_WITH` and `ENDS_WITH` compare code points, and `STARTS_WITH` can use a column's btree index. `ILIKE`, `ISTARTS_WITH`, `IENDS_WITH` and `IEQ` fold both sides with the Unicode simple case foldings that MongoDB's case-insensitive matching applies, through one expression, `translate(lower((x) COLLATE "pg_catalog"."und-x-icu"), from, to)`, where `translate()` maps the 22 lowercase variants that ICU `lower()` keeps, such as `ſ`, `µ` and the final sigma `ς`, to their usual letters; so each returns every row that its case-sensitive form returns, and `ISTARTS_WITH "ΚΑΣ"` finds `ΚΑΣΤΡΟ`. The first three match a `LIKE` pattern whose `%`, `_` and `\` are escaped. The two backends still differ on `İ`, which ICU lowers to `i` followed by U+0307, and on letters newer than one side's Unicode tables. The fold needs a `UTF8` database on a server built with ICU: on another, such as one in `LATIN1`, these four operators fail with `UNSUPPORTED_QUERY` (400), and every other operation works. Indexes serve them only on exactly the compiled expression, which the exported `caseFoldExpression(sqlExpression)` returns, such as `caseFoldExpression('"name"')`: a btree with `text_pattern_ops` on it serves `IEQ` and `ISTARTS_WITH`, and a `pg_trgm` GIN index with `gin_trgm_ops` serves `ILIKE`, `ISTARTS_WITH` and `IENDS_WITH`. The [queries guide](https://simtlix.github.io/simfinity.js/guide/queries.html#case-folding) gives the details and the index statements.
82
+ The text operators take literal values as bound parameters. `LIKE`, `STARTS_WITH` and `ENDS_WITH` compare code points, and `STARTS_WITH` can use a column's btree index. `ILIKE`, `ISTARTS_WITH`, `IENDS_WITH` and `IEQ` fold both sides with the Unicode simple case foldings that MongoDB's case-insensitive matching applies, through one expression, `translate(lower((x) COLLATE "pg_catalog"."und-x-icu"), from, to)`, where `translate()` maps the 22 lowercase variants that ICU `lower()` keeps, such as `ſ`, `µ` and the final sigma `ς`, to their usual letters; so each returns every row that its case-sensitive form returns, and `ISTARTS_WITH "ΚΑΣ"` finds `ΚΑΣΤΡΟ`. The first three match a `LIKE` pattern whose `%`, `_` and `\` are escaped. The two backends still differ on `İ`, which ICU lowers to `i` followed by U+0307, and on letters newer than one side's Unicode tables. `IIN`, `IN` ignoring case, folds both sides the same way, as `fold("column") = ANY (ARRAY(SELECT fold(w.value) FROM unnest($1::text[]) w(value)))` with its values bound as one array, and compiles `IIN []` to `FALSE`. The fold needs a `UTF8` database on a server built with ICU: on another, such as one in `LATIN1`, these four operators and `IIN` fail with `UNSUPPORTED_QUERY` (400), and every other operation works. Indexes serve them only on exactly the compiled expression, which the exported `caseFoldExpression(sqlExpression)` returns, such as `caseFoldExpression('"name"')`: a btree with `text_pattern_ops` on it serves `IEQ`, `IIN` and `ISTARTS_WITH`, and a `pg_trgm` GIN index with `gin_trgm_ops` serves `ILIKE`, `ISTARTS_WITH` and `IENDS_WITH`. [`CONTAINS_ALL`](https://simtlix.github.io/simfinity.js/guide/queries.html#filters-on-lists) compiles to `"column" @> $1::type[]` on a list of the root record, which a GIN index of your own on the column serves, and initialization keeps such an index in both modes; lists inside embedded values are scanned. The [queries guide](https://simtlix.github.io/simfinity.js/guide/queries.html#case-folding) gives the details and the index statements.
81
83
 
82
84
  `IS_EMPTY` and `IS_NOT_EMPTY` take no value and test the queried record's own fields: a missing, null or `""` String, a missing or null value of any other scalar, a missing, null or `[]` list, and an embedded object that is missing, null or has every stored member empty. They compile from where the path is stored and bind no parameters: `(c IS NULL OR c = '')` for a String column, so that a btree serves it through a `BitmapOr`, `c IS NULL` for any other column, including the uuid identity of a record whose `id` is declared `String`, and `COALESCE(cardinality(c), 0) = 0` for a native array, also on owned rows; a jsonb member is read as text with `->>` and never cast, a jsonb list through a `jsonb_typeof` guard, and a jsonb object as SQL `NULL`, JSON `null` or all of its members empty; an owned single object is empty when its state is not `present` or no owned row has a member that is not empty, and an owned list when its state is not `present` or it has no row, null items included, with a subquery alias of its own at each level of owned storage. `IS_NOT_EMPTY` is the positive form of a leaf and the `NOT` of an object or owned form, so every record matches exactly one of the two. An emptiness path that continues inside an embedded list, such as `lines.qty`, fails with `INVALID_FILTER_PATH` (400), since emptiness of an item inside that list is not defined. Through a collection, use an explicit frame and test the element's own field (for example `ANY seasons` with `year IS_EMPTY`); the flattened `seasons.year IS_EMPTY` shorthand is excluded. The [queries guide](https://simtlix.github.io/simfinity.js/guide/queries.html) describes the rules.
83
85
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-postgres",
3
- "version": "3.15.0",
3
+ "version": "3.16.0",
4
4
  "description": "PostgreSQL GraphQL runtime and schema generation with foreign keys for Simfinity",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -27,8 +27,8 @@
27
27
  "directory": "packages/postgres"
28
28
  },
29
29
  "dependencies": {
30
- "@simtlix/simfinity-core": "3.15.0",
31
- "@simtlix/simfinity-sql": "3.15.0",
30
+ "@simtlix/simfinity-core": "3.16.0",
31
+ "@simtlix/simfinity-sql": "3.16.0",
32
32
  "pg": "^8.16.3"
33
33
  },
34
34
  "peerDependencies": {
package/src/codecs.js CHANGED
@@ -77,7 +77,7 @@ export const isPostgresError = (error) => typeof error?.code === 'string' && /^[
77
77
 
78
78
  // A statement that folds case on a database that cannot evaluate the fold (see isCaseFoldUnsupported): the request
79
79
  // uses an operator that this database cannot run, while every other operation keeps working.
80
- const CASE_FOLD_UNSUPPORTED = ['ILIKE, ISTARTS_WITH, IENDS_WITH and IEQ need a UTF8 database with the ICU collation und-x-icu', 'UNSUPPORTED_QUERY', 400];
80
+ const CASE_FOLD_UNSUPPORTED = ['ILIKE, ISTARTS_WITH, IENDS_WITH, IEQ and IIN need a UTF8 database with the ICU collation und-x-icu', 'UNSUPPORTED_QUERY', 400];
81
81
 
82
82
  /** Maps PostgreSQL errors (and exhausted retries) without SQL details; returns every other value unchanged. */
83
83
  export const normalizeDatabaseError = (error) => {
package/src/index.js CHANGED
@@ -22,7 +22,7 @@ export {
22
22
  /** Each instance permanently binds one pool/schema; getModel exposes PostgreSQL native helpers. */
23
23
  export const createPostgres = (options) => createSQL({ plugin: postgresPlugin(options) });
24
24
  const defaultInstance = createPostgres();
25
- export const { configureQueryLimits, configureMutationLimits, configureAfterCommit, configure, connect, addNoEndpointType, createSchema, getModel, getType, getInputType, getRegistrations, use, registerMutation, registerCustomQuery, registerView, saveObject, preventCreatingCollection, withTransaction } = defaultInstance;
25
+ export const { configureQueryLimits, configureMutationLimits, configureAfterCommit, configure, connect, addNoEndpointType, createSchema, getModel, getType, getInputType, getRegistrations, use, registerMutation, registerCustomQuery, registerView, saveObject, updateObject, deleteObject, transitionObject, preventCreatingCollection, withTransaction } = defaultInstance;
26
26
 
27
27
  /** Preserve the foundation low-level API while allowing initialization of the configured default instance. */
28
28
  export const initializeDatabase = (poolOrOptions, description, options) => description
package/src/plugin.js CHANGED
@@ -24,7 +24,7 @@ export const postgresPlugin = (options) => ({
24
24
  // declaration names the compileQuery it describes, so a copy of this plugin with another compileQuery compiles the
25
25
  // operators of 3.7.0, without reference emptiness or quantifiers, until it declares its own.
26
26
  queryFeatures: {
27
- operators: ['EQ', 'NE', 'LT', 'LTE', 'GT', 'GTE', 'BTW', 'IN', 'NIN', 'LIKE', 'ILIKE', 'STARTS_WITH', 'ISTARTS_WITH', 'ENDS_WITH', 'IENDS_WITH', 'IEQ', 'IS_EMPTY', 'IS_NOT_EMPTY'],
27
+ operators: ['EQ', 'NE', 'LT', 'LTE', 'GT', 'GTE', 'BTW', 'IN', 'NIN', 'LIKE', 'ILIKE', 'STARTS_WITH', 'ISTARTS_WITH', 'ENDS_WITH', 'IENDS_WITH', 'IEQ', 'IS_EMPTY', 'IS_NOT_EMPTY', 'CONTAINS_ALL', 'IIN'],
28
28
  referenceEmptiness: true,
29
29
  quantifiers: true,
30
30
  compileQuery,
@@ -1,4 +1,4 @@
1
- // The case fold of the case-insensitive text operators (ILIKE, ISTARTS_WITH, IENDS_WITH and IEQ), the shared
1
+ // The case fold of the case-insensitive operators (ILIKE, ISTARTS_WITH, IENDS_WITH, IEQ and IIN), the shared
2
2
  // contract of #108 section 3.1: the Unicode simple case foldings that MongoDB's case-insensitive regular expressions
3
3
  // apply. lower() under ICU's root collation applies them to every script the same way on every server, whatever the
4
4
  // database's default collation (text paths are `C` columns, under which lower() folds ASCII only), except for the
@@ -47,9 +47,9 @@ const FROM = `'${CASE_FOLD_MAPPINGS.map(([from]) => from).join('')}'`;
47
47
  const TO = `'${CASE_FOLD_MAPPINGS.map(([, to]) => to).join('')}'`;
48
48
 
49
49
  /**
50
- * The SQL text that folds `sqlExpression` for the case-insensitive text operators, exactly as the compiler writes it on
51
- * both sides of each comparison: `translate(lower((<expression>) COLLATE "pg_catalog"."und-x-icu"), '<22 characters>',
52
- * '<their simple case foldings>')`. Applications build their expression indexes with it, such as
50
+ * The SQL text that folds `sqlExpression` for the case-insensitive text operators and IIN, exactly as the compiler
51
+ * writes it on both sides of each comparison: `translate(lower((<expression>) COLLATE "pg_catalog"."und-x-icu"),
52
+ * '<22 characters>', '<their simple case foldings>')`. Applications build their expression indexes with it, such as
53
53
  * `CREATE INDEX ... (${caseFoldExpression('"name"')} text_pattern_ops)`. The expression is trusted SQL, inserted as
54
54
  * given: quote identifiers yourself and never pass request input. It goes in parentheses, because COLLATE binds more
55
55
  * tightly than `->>` and every other binary operator: without them, `"details" ->> 'label'` would collate only
@@ -1,6 +1,7 @@
1
1
  import { QLQuantifier, resolveModelPath, SimfinityError } from '@simtlix/simfinity-core';
2
2
  import {
3
- crossingKeys, describeEmptiness, describeModelEmptiness, emptinessScopePaths, isEmptinessOperator, textQueryOperator, validateQuantifiedGroups, validateTextFilterValue,
3
+ assertContainsAllPath, crossingKeys, describeEmptiness, describeModelEmptiness, emptinessScopePaths, isEmptinessOperator, textQueryOperator,
4
+ validateContainsAllValue, validateQuantifiedGroups, validateTextFilterValue, validateTextListFilterValue,
4
5
  } from '@simtlix/simfinity-core/internal/query-operators';
5
6
  import { identifier as q, literal, qualified } from '../schema/sql.js';
6
7
  import { encodeScalar, storageTypes, invalidValue } from '../codecs.js';
@@ -347,6 +348,22 @@ const compileSelect = (models, database, plan, extra, configuration, batched) =>
347
348
  return operator === 'IN' ? expressionSQL : `NOT ${expressionSQL}`;
348
349
  }
349
350
  if (operator === 'BTW') return `(${predicate(expression, 'GTE', value[0], negated)} AND ${predicate(expression, 'LTE', value[1], negated)})`;
351
+ if (operator === 'CONTAINS_ALL') {
352
+ validateContainsAllValue(value);
353
+ // One array parameter of the path's element type, cast like IN elements: set containment, as MongoDB's $all. NULL
354
+ // elements of the stored list are ignored, and a NULL list gives NULL, which only the negated form turns into false.
355
+ const contains = `${sql} @> ${bind(value.map(encodeValue), `${type}[]`)}`;
356
+ return negated ? `COALESCE(${contains}, FALSE)` : contains;
357
+ }
358
+ if (operator === 'IIN') {
359
+ validateTextListFilterValue(operator, value, field.scalar);
360
+ if (!value.length) return 'FALSE';
361
+ // The values are folded by the server, once per statement, so that application indexes on the folded column serve it.
362
+ const members = `ANY (ARRAY(SELECT ${fold('w.value')} FROM unnest(${bind(value, 'text[]')}) w(value)))`;
363
+ if (list) return `EXISTS (SELECT 1 FROM unnest(${sql}) v(value) WHERE ${fold('v.value')} = ${members})`;
364
+ const compare = `${fold(expression.uncollated)} = ${members}`;
365
+ return negated ? `COALESCE(${compare}, FALSE)` : compare;
366
+ }
350
367
  if (value == null || Array.isArray(value)) invalidValue(`${operator} requires a non-null scalar value`);
351
368
  const text = textQueryOperator(operator);
352
369
  if (text) return textPredicate(expression, operator, text, value, negated);
@@ -598,6 +615,9 @@ const compileSelect = (models, database, plan, extra, configuration, batched) =>
598
615
  if (!node) return 'TRUE';
599
616
  if (node.kind === 'quantified') return quantified(node, frame);
600
617
  if (node.kind === 'predicate' && isEmptinessOperator(node.operator)) return `(${emptiness(node, frame)})`;
618
+ // CONTAINS_ALL's path and leaf rules (#108 part 4), with core's codes, before any join or alias, so that a plan built
619
+ // by hand never compiles to the legacy collection join or the multiplying vector join, or raises 42883.
620
+ if (node.kind === 'predicate' && node.operator === 'CONTAINS_ALL') assertContainsAllPath(resolveModelPath(models, frame.entity, node.path));
601
621
  if (node.kind === 'predicate') {
602
622
  // Check ordinary paths and values before the new client metadata, without creating joins or aliases.
603
623
  if (Object.hasOwn(node, 'joins')) validateQuantifiedGroups({ AND: [{ conditions: [{ field: node.path.join('.'), operator: node.operator, value: node.value }] }] }, { models, entity: frame.entity, referenceEmptiness: true, quantifiers: true });
@@ -609,7 +629,9 @@ const compileSelect = (models, database, plan, extra, configuration, batched) =>
609
629
  const leaf = steps.at(-1);
610
630
  const identity = ['id', '_id'].includes(leaf.name) && (steps.length === 1 ? !frame.table.ownership : steps.at(-2).kind !== 'embedded');
611
631
  const field = operandField(leaf, identity);
612
- predicate({ field, type: storageTypes[field.scalar], list: field.list, sql: '', uncollated: '' }, node.operator, node.value, negated);
632
+ // The placeholders are never emitted: detached() follows. They must be non-empty, because textPredicate and the IIN
633
+ // branch fold `uncollated` with caseFoldExpression, which rejects an empty expression (case-fold.js:60).
634
+ predicate({ field, type: storageTypes[field.scalar], list: field.list, sql: 'NULL', uncollated: 'NULL' }, node.operator, node.value, negated);
613
635
  detached(checked.reason);
614
636
  }
615
637
  const sql = predicate(path(node.path, 'filter', frame, checked.joins), node.operator, node.value, negated);
package/types/index.d.ts CHANGED
@@ -246,9 +246,10 @@ export interface PostgresRuntime extends Runtime<PostgresModel, PostgresSession>
246
246
  }
247
247
  /**
248
248
  * PostgreSQL 15+ plugin for createSQL; omitted options allow later runtime.configure. Its `queryFeatures` lists every
249
- * `QLOperator` value. The case-insensitive text operators fold case with {@link caseFoldExpression}, which needs a UTF8
250
- * database with the ICU collation `und-x-icu`: on another database, such as one in LATIN1 or on a server built without
251
- * ICU, they fail with UNSUPPORTED_QUERY (400), and every other operation works.
249
+ * `QLOperator` value. The case-insensitive text operators and `IIN` fold case with {@link caseFoldExpression}, which
250
+ * needs a UTF8 database with the ICU collation `und-x-icu`: on another database, such as one in LATIN1 or on a server
251
+ * built without ICU, they fail with UNSUPPORTED_QUERY (400), except `IIN []`, which compiles to `FALSE` without a fold,
252
+ * and every other operation works.
252
253
  *
253
254
  * `IS_EMPTY` and `IS_NOT_EMPTY` compile from where a path is stored, binding no parameters: `(c IS NULL OR c = '')`
254
255
  * for a String column and `c IS NULL` for any other, the uuid identity included; `COALESCE(cardinality(c), 0) = 0`
@@ -293,7 +294,7 @@ export function postgresPlugin(options?: PostgresConfiguration): SQLPlugin<
293
294
  >;
294
295
  export function createPostgres(options?: PostgresConfiguration): PostgresRuntime;
295
296
  /**
296
- * The SQL text with which ILIKE, ISTARTS_WITH, IENDS_WITH and IEQ fold both sides of their comparison, for an SQL
297
+ * The SQL text with which ILIKE, ISTARTS_WITH, IENDS_WITH, IEQ and IIN fold both sides of their comparison, for an SQL
297
298
  * expression such as a quoted column name or a jsonb member: `translate(lower((<expression>) COLLATE
298
299
  * "pg_catalog"."und-x-icu"), '<22 characters>', '<their simple case foldings>')`, the Unicode simple case foldings that
299
300
  * MongoDB's case-insensitive matching applies. Indexes serve these operators only on exactly this expression, so build
@@ -317,6 +318,9 @@ export const registerMutation: PostgresRuntime['registerMutation'];
317
318
  export const registerCustomQuery: PostgresRuntime['registerCustomQuery'];
318
319
  export const registerView: PostgresRuntime['registerView'];
319
320
  export const saveObject: PostgresRuntime['saveObject'];
321
+ export const updateObject: PostgresRuntime['updateObject'];
322
+ export const deleteObject: PostgresRuntime['deleteObject'];
323
+ export const transitionObject: PostgresRuntime['transitionObject'];
320
324
  export const preventCreatingCollection: PostgresRuntime['preventCreatingCollection'];
321
325
  export const withTransaction: PostgresRuntime['withTransaction'];
322
326
  /** Initializes the default module instance, after configure/connect/createSchema. */
@@ -325,6 +329,6 @@ export function initializeDatabase(options?: InitializationOptions): Promise<Ini
325
329
  export const configureQueryLimits: Runtime['configureQueryLimits'];
326
330
  /**
327
331
  * The same process-wide setter as every runtime's configureMutationLimits. Unknown or misspelled
328
- * options throw INVALID_MUTATION_LIMITS (400) and keep the current limit.
332
+ * options throw INVALID_MUTATION_LIMITS (400) and keep the current limits.
329
333
  */
330
334
  export const configureMutationLimits: Runtime['configureMutationLimits'];