@rocicorp/zero 1.10.0-canary.18 → 1.10.0-canary.19

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 (99) hide show
  1. package/out/zero/package.js +1 -1
  2. package/out/zero/package.js.map +1 -1
  3. package/out/zero-cache/src/config/zero-config.d.ts +2 -19
  4. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  5. package/out/zero-cache/src/config/zero-config.js +28 -87
  6. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  7. package/out/zero-cache/src/db/create.d.ts.map +1 -1
  8. package/out/zero-cache/src/db/create.js +1 -4
  9. package/out/zero-cache/src/db/create.js.map +1 -1
  10. package/out/zero-cache/src/db/lite-tables.d.ts +2 -10
  11. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  12. package/out/zero-cache/src/db/lite-tables.js +3 -8
  13. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  14. package/out/zero-cache/src/db/specs.d.ts +0 -41
  15. package/out/zero-cache/src/db/specs.d.ts.map +1 -1
  16. package/out/zero-cache/src/db/specs.js +3 -30
  17. package/out/zero-cache/src/db/specs.js.map +1 -1
  18. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  19. package/out/zero-cache/src/server/change-streamer.js +1 -2
  20. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  21. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  22. package/out/zero-cache/src/server/replicator.js +2 -6
  23. package/out/zero-cache/src/server/replicator.js.map +1 -1
  24. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +1 -11
  25. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  26. package/out/zero-cache/src/services/change-source/pg/change-source.js +2 -21
  27. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  28. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +0 -1
  29. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  30. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +6 -7
  31. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  32. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +229 -1513
  33. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  34. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +2 -2
  35. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  36. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +1 -1
  37. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  38. package/out/zero-cache/src/services/change-source/pg/schema/init.js +6 -15
  39. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  40. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +20 -137
  41. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
  42. package/out/zero-cache/src/services/change-source/pg/schema/published.js +13 -82
  43. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  44. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +21 -128
  45. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  46. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +5 -5
  47. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  48. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +0 -2
  49. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  50. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +0 -3
  51. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  52. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +0 -1
  53. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  54. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  55. package/out/zero-cache/src/services/litestream/commands.js +3 -8
  56. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  57. package/out/zero-cache/src/services/litestream/config-v5.yml +1 -15
  58. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  59. package/out/zero-cache/src/services/replicator/change-processor.js +16 -16
  60. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  61. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  62. package/out/zero-cache/src/services/replicator/incremental-sync.js +0 -1
  63. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  64. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +2 -27
  65. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
  66. package/out/zero-cache/src/services/replicator/write-worker-client.js +2 -3
  67. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  68. package/out/zero-cache/src/services/replicator/write-worker.js +5 -24
  69. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  70. package/out/zero-cache/src/workers/replicator.d.ts +0 -1
  71. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  72. package/out/zero-cache/src/workers/replicator.js +2 -4
  73. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  74. package/out/zero-client/src/client/version.js +1 -1
  75. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  76. package/out/zqlite/src/query-builder.js +1 -2
  77. package/out/zqlite/src/query-builder.js.map +1 -1
  78. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
  79. package/out/zqlite/src/sqlite-stat-fanout.js +0 -1
  80. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  81. package/out/zqlite/src/table-source.d.ts +1 -1
  82. package/out/zqlite/src/table-source.d.ts.map +1 -1
  83. package/out/zqlite/src/table-source.js +1 -2
  84. package/out/zqlite/src/table-source.js.map +1 -1
  85. package/package.json +1 -1
  86. package/out/zero-cache/src/db/index-predicate.d.ts +0 -4
  87. package/out/zero-cache/src/db/index-predicate.d.ts.map +0 -1
  88. package/out/zero-cache/src/db/index-predicate.js +0 -46
  89. package/out/zero-cache/src/db/index-predicate.js.map +0 -1
  90. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +0 -19
  91. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +0 -1
  92. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +0 -581
  93. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +0 -1
  94. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +0 -131
  95. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +0 -1
  96. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +0 -70
  97. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +0 -1
  98. package/out/zero-cache/src/services/litestream/litestream-controller.js +0 -101
  99. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ddl.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ddl.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,wCAAwC,CAAC;AAC5D,OAAO,EAAiB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAU7E,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAkBlC,eAAO,MAAM,cAAc;;;;;;;;;;aAgBzB,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAc9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAU/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAIpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAIlC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAYtE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,WAAW,UAwM/D;AAGD,eAAO,MAAM,IAAI,2HAQP,CAAC;AAEX,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,WAAW,UAoC9D;AAGD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,MAAM,UAMzB"}
@@ -94,7 +94,7 @@ var DDL_SERIALIZATION_LOCK = 4353719051050729648n;
94
94
  * Instead, we opt for the simplicity and isolation of having each shard
95
95
  * completely own (and maintain) the entirety of its trigger/function stack.
96
96
  */
97
- function createEventFunctionStatements(shard, includePartialIndexes = true) {
97
+ function createEventFunctionStatements(shard) {
98
98
  const { appID, shardNum, publications } = shard;
99
99
  const schema = id(upstreamSchema(shard));
100
100
  return `
@@ -128,7 +128,7 @@ CREATE FUNCTION ${schema}.schema_specs()
128
128
  RETURNS JSON
129
129
  STABLE
130
130
  AS $$
131
- ${publishedSchemaQuery(publications, includePartialIndexes)}
131
+ ${publishedSchemaQuery(publications)}
132
132
  $$ LANGUAGE sql;
133
133
 
134
134
 
@@ -1 +1 @@
1
- {"version":3,"file":"ddl.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"sourcesContent":["import {literal as lit} from 'pg-format';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {publishedSchema, publishedSchemaQuery} from './published.ts';\n\n// Sent in the 'version' tag of \"ddlStart\" and \"ddlUpdate\" event messages.\n// This is used to ensure that the message constructed in the upstream\n// Trigger function is compatible with the code processing it in the zero-cache.\n//\n// Increment this when changing the format of the contents of the \"ddl\" events.\n// This will allow old / incompatible code to detect the change and abort.\nexport const PROTOCOL_VERSION = 1;\n\n// In protocol v2 (planned, not yet emitted), \"ddlStart\" events that are not\n// associated with a schema change will be context-only, omitting the\n// `schema` snapshot entirely. This avoids bloating the WAL with (large)\n// redundant schema snapshots for DDL commands that do not affect the\n// published schema, e.g. the CREATE/ALTER/DROP TABLE sub-commands executed\n// by REFRESH MATERIALIZED VIEW CONCURRENTLY.\n//\n// This release already accepts v2 events so that the (subsequent) release\n// that upgrades the triggers to emit them remains rollback safe with respect\n// to this one.\nconst versionSchema = v.literalUnion(PROTOCOL_VERSION, 2);\n\nconst triggerEvent = v.object({\n context: v.object({query: v.string()}).rest(v.string()),\n});\n\nexport const ddlEventSchema = triggerEvent.extend({\n version: versionSchema,\n event: v.object({tag: v.string()}),\n // Maps the OID of each published table to the `attnum`s of published\n // columns that were created in the (upstream) transaction that emitted\n // the event. Such columns are guaranteed to have the column default in\n // all pre-existing rows, and can thus be replicated without backfill if\n // the default value itself is replicable. Tables whose publication\n // entries (e.g. column lists) were modified in the same transaction are\n // excluded, since a newly *published* (as opposed to newly *created*)\n // column may hold arbitrary values in existing rows.\n //\n // The field is absent in messages from older versions of the upstream\n // functions (in which case backfill decisions fall back to the command\n // tag heuristic), and `null` when there are no such columns.\n newColumns: v.record(v.array(v.number())).nullable().optional(),\n});\n\n/**\n * A {@link DdlStartEvent} message is emitted before every DDL event,\n * containing the command `tag`.\n *\n * In most cases, the `DdlStartEvent` itself will not be associated with a\n * schema change, in which case the event is context-only (in protocol v2,\n * planned, both `previousSchema` and `schema` will be absent; in v1,\n * `schema` is set and `previousSchema` is `null`). The message is still\n * emitted to provide the command `tag` context in case an immediately\n * following `DdlStartEvent` tag is emitted with a schema change (which can\n * happen when another event trigger results in a nested ddl statement).\n *\n * In such cases, the `previousSchema` and `schema` fields of the latter event\n * are used to determine the necessary schema change operations (as they are\n * with `ddlUpdate` and `schemaSnapshot` events), and the `tag` of the\n * preceding start event indicates the command that precipitated the schema\n * change (e.g. a CREATE vs ALTER) to determine whether a backfill is\n * necessary.\n */\nexport const ddlStartEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlStart'),\n // Set (along with `previousSchema`) only if the ddlStart event itself is\n // associated with a schema change. Absent in context-only (protocol v2,\n // planned) events. v1 events always contain the current `schema`.\n schema: publishedSchema.optional(),\n // For ddlStart messages, previousSchema is `null` (v1) or absent\n // (protocol v2, planned) if there was no change in schema detected.\n previousSchema: publishedSchema.nullable().optional(),\n // For backwards compatibility with previous versions of the trigger,\n // default an absent `event` field with a semantic equivalent. This\n // field override can be removed in a version that is rollback safe\n // with 1.4.0.\n event: v.object({tag: v.string()}).optional(() => ({tag: 'UNKNOWN'})),\n});\n\nexport type DdlStartEvent = v.Infer<typeof ddlStartEventSchema>;\n\n/**\n * A {@link DdlUpdateEvent} is emitted if there was a change in the schema.\n * It always contains `previousSchema` and (current) `schema` fields, leaving\n * it to the receiver to compute the necessary schema change operations.\n */\nexport const ddlUpdateEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlUpdate'),\n // ddlUpdate messages are only emitted if the schema changed. `schema`\n // contains the current (i.e. post-change) snapshot.\n schema: publishedSchema,\n // The `previousSchema` contains the schema before the change.\n //\n // In 1.5.0 it is always set, and can be made non-optional when\n // rollback safe.\n previousSchema: publishedSchema.optional(),\n});\n\nexport type DdlUpdateEvent = v.Infer<typeof ddlUpdateEventSchema>;\n\n/**\n * The `schemaSnapshot` message is a snapshot of a schema taken in response to\n * a `COMMENT ON PUBLICATION` command, which is a hook recognized by zero\n * to manually emit `previousSchema` and `schema` snapshots when a difference\n * is detected. This is a workaround provided to support detection of schema\n * changes from `ALTER PUBLICATION` commands on supabase, which does not fire\n * event triggers for them (https://github.com/supabase/supautils/issues/123).\n *\n * The hook is exercised by trailing the publication change with a\n * `COMMENT ON PUBLICATION` statement, e.g.\n *\n * ```sql\n * BEGIN;\n * ALTER PUBLICATION my_publication ...;\n * COMMENT ON PUBLICATION my_publication IS 'whatever';\n * COMMIT;\n * ```\n *\n * Note that it is fine to invoke `COMMENT ON PUBLICATION` statements\n * on a database that *does* support event triggers on\n * `ALTER PUBLICATION` statements, as it will simply be a no-op.\n */\nexport const schemaSnapshotEventSchema = ddlEventSchema.extend({\n type: v.literal('schemaSnapshot'),\n schema: publishedSchema,\n previousSchema: publishedSchema.optional(),\n});\n\nexport type SchemaSnapshotEvent = v.Infer<typeof schemaSnapshotEventSchema>;\n\nexport const replicationEventSchema = v.union(\n ddlStartEventSchema,\n ddlUpdateEventSchema,\n schemaSnapshotEventSchema,\n);\n\nexport type ReplicationEvent = v.Infer<typeof replicationEventSchema>;\n\n// Creates a function that appends `_{shard-num}` to the input and\n// quotes the result to be a valid identifier.\nfunction append(shardNum: number) {\n return (name: string) => id(name + '_' + String(shardNum));\n}\n\n// pg_advisory_xact_lock key for serializing ddl statements in order to\n// produce correct schema change diffs.\nconst DDL_SERIALIZATION_LOCK = 0x3c6b8468f1bac0b0n;\n\n/**\n * Event trigger functions contain the core logic that are invoked by triggers.\n *\n * Note that although many of these functions can theoretically be parameterized and\n * shared across shards, it is advantageous to keep the functions in each shard\n * isolated from each other in order to avoid the complexity of shared-function\n * versioning.\n *\n * In a sense, shards (and their triggers and functions) should be thought of as\n * execution environments that can be updated at different schedules. If per-shard\n * triggers called into shared functions, we would have to consider versioning the\n * functions when changing their behavior, backwards compatibility, removal of\n * unused versions, etc. (not unlike versioning of npm packages).\n *\n * Instead, we opt for the simplicity and isolation of having each shard\n * completely own (and maintain) the entirety of its trigger/function stack.\n */\nexport function createEventFunctionStatements(\n shard: ShardConfig,\n includePartialIndexes = true,\n) {\n const {appID, shardNum, publications} = shard;\n const schema = id(upstreamSchema(shard)); // e.g. \"{APP_ID}_{SHARD_ID}\"\n return /*sql*/ `\nCREATE SCHEMA IF NOT EXISTS ${schema};\n\nCREATE OR REPLACE FUNCTION ${schema}.get_trigger_context()\nRETURNS record AS $$\nDECLARE\n result record;\nBEGIN\n SELECT COALESCE(current_query(), 'current_query() returned NULL') AS \"query\" into result;\n RETURN result;\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.notice_ignore(reason TEXT, tag TEXT, target record)\nRETURNS void AS $$\nBEGIN\n RAISE NOTICE '${appID}_${shardNum} ignoring % % %', reason, tag, \n COALESCE(row_to_json(target)::text, '');\nEND\n$$ LANGUAGE plpgsql;\n\n\n-- Note: DROP and CREATE to upgrade from v20 to v21 because the\n-- return type has changed. This can be simplified to CREATE OR REPLACE\n-- once 1.5.0 is rollback safe.\nDROP FUNCTION IF EXISTS ${schema}.schema_specs();\nCREATE FUNCTION ${schema}.schema_specs()\nRETURNS JSON \nSTABLE\nAS $$\n ${publishedSchemaQuery(publications, includePartialIndexes)}\n$$ LANGUAGE sql;\n\n\n-- Stores the most recent published schema\nCREATE TABLE IF NOT EXISTS ${schema}.\"publishedSchema\" (\n current JSON,\n exists BOOL PRIMARY KEY DEFAULT true CHECK (exists)\n);\n\nINSERT INTO ${schema}.\"publishedSchema\" (current) VALUES (${schema}.schema_specs())\n ON CONFLICT (exists) DO \n UPDATE SET current = excluded.current;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.update_schemas(event_type text, tag text, target record)\nRETURNS void AS $$\nDECLARE\n prev_schema_specs JSON;\n schema_specs JSON;\n message TEXT;\nBEGIN\n SELECT current FROM ${schema}.\"publishedSchema\" INTO prev_schema_specs;\n SELECT ${schema}.schema_specs() INTO schema_specs;\n \n IF prev_schema_specs::text != schema_specs::text THEN\n UPDATE ${schema}.\"publishedSchema\" SET current = schema_specs;\n ELSIF event_type = 'ddlStart' THEN\n -- ddlStart events are always be emitted to allow the zero-cache\n -- to track the context of the current command tag in the face of\n -- nested event triggers (e.g. start->start->end->end).\n prev_schema_specs = NULL;\n ELSIF event_type = 'ddlUpdate' THEN\n -- TODO: fold 'schemaSnapshot' into this condition too (i.e. make it \"ELSE\")\n -- when 1.5.0 is rollback safe. Until then, noop schemaSnapshots are sent\n -- for compatibility with 1.0.0 ~ 1.4.0.\n PERFORM ${schema}.notice_ignore('noop', tag, target);\n RETURN;\n END IF;\n\n SELECT json_build_object(\n 'type', event_type,\n 'version', ${PROTOCOL_VERSION},\n 'previousSchema', prev_schema_specs,\n 'schema', schema_specs,\n 'event', json_build_object('tag', tag),\n 'context', ${schema}.get_trigger_context()\n ) INTO message;\n\n PERFORM pg_logical_emit_message(true, '${appID}/${shardNum}/ddl', message);\n\n RAISE NOTICE 'Emitted ${appID}_${shardNum} % for % %', event_type, tag,\n COALESCE(row_to_json(target)::text, '');\nEND\n$$ LANGUAGE plpgsql;\n\n\n-- Hook/workaround to manually trigger replication of schema changes on DBs \n-- that do not support/allow event triggers.\nCREATE OR REPLACE FUNCTION ${schema}.update_schemas()\nRETURNS void AS $$\nBEGIN\n PERFORM ${schema}.update_schemas('schemaSnapshot', 'MANUAL', NULL);\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_start()\nRETURNS event_trigger AS $$\nDECLARE\n schema_specs JSON;\n message TEXT;\nBEGIN\n -- serialize DDL statements to compute correct schema change diffs\n PERFORM pg_advisory_xact_lock(${DDL_SERIALIZATION_LOCK});\n PERFORM ${schema}.update_schemas('ddlStart', TG_TAG, NULL);\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_end()\nRETURNS event_trigger AS $$\nDECLARE\n publications TEXT[];\n target RECORD;\n relevant RECORD;\n schema_specs JSON;\n message TEXT;\n event TEXT;\nBEGIN\n publications := ARRAY[${lit(publications)}];\n\n SELECT objid, object_type, object_identity \n FROM pg_event_trigger_ddl_commands() \n LIMIT 1 INTO target;\n\n -- Filter DDL updates that are not relevant to the shard (i.e. publications) when possible.\n SELECT true INTO relevant;\n\n -- Note: ALTER TABLE statements may *remove* the table from the set of published\n -- tables, and there is no way to determine if the table \"used to be\" in the\n -- set. Thus, all ALTER TABLE statements must produce a ddl update, similar to\n -- any DROP * statement.\n IF (target.object_type = 'table' AND TG_TAG != 'ALTER TABLE') \n OR target.object_type = 'table column' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'index' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_indexes as ind ON ind.schemaname = ns.nspname AND ind.indexname = c.relname\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = ind.tablename\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication relation' THEN\n SELECT pb.pubname FROM pg_publication_rel AS rel\n JOIN pg_publication AS pb ON pb.oid = rel.prpubid\n WHERE rel.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'publication namespace' THEN\n SELECT pb.pubname FROM pg_publication_namespace AS ns\n JOIN pg_publication AS pb ON pb.oid = ns.pnpubid\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'schema' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication' THEN\n SELECT 1 WHERE target.object_identity = ANY (publications)\n INTO relevant;\n\n -- no-op CREATE IF NOT EXIST statements\n ELSIF TG_TAG LIKE 'CREATE %' AND target.object_type IS NULL THEN\n relevant := NULL;\n END IF;\n\n IF relevant IS NULL THEN\n PERFORM ${schema}.notice_ignore('irrelevant', TG_TAG, target);\n RETURN;\n END IF;\n\n IF TG_TAG = 'COMMENT' THEN\n -- Only make schemaSnapshots for COMMENT ON PUBLICATION\n IF target.object_type != 'publication' THEN\n PERFORM ${schema}.notice_ignore('irrelevant', TG_TAG, target);\n RETURN;\n END IF;\n PERFORM ${schema}.update_schemas('schemaSnapshot', TG_TAG, target);\n ELSE\n PERFORM ${schema}.update_schemas('ddlUpdate', TG_TAG, target);\n END IF;\n\nEND\n$$ LANGUAGE plpgsql;\n`;\n}\n\n// Exported for testing.\nexport const TAGS = [\n 'CREATE TABLE',\n 'ALTER TABLE',\n 'CREATE INDEX',\n 'DROP TABLE',\n 'DROP INDEX',\n 'ALTER PUBLICATION',\n 'ALTER SCHEMA',\n] as const;\n\nexport function createEventTriggerStatements(shard: ShardConfig) {\n // Better to assert here than get a cryptic syntax error from Postgres.\n assert(shard.publications.length, `shard publications must be non-empty`);\n\n // Unlike functions, which are namespaced in shard-specific schemas,\n // EVENT TRIGGER names are in the global namespace and thus must include\n // the appID and shardNum.\n const {appID, shardNum} = shard;\n const sharded = append(shardNum);\n const schema = id(upstreamSchema(shard));\n\n const triggers = [\n dropEventTriggerStatements(shard.appID, shard.shardNum),\n /*sql*/ `\nCREATE EVENT TRIGGER ${sharded(`${appID}_ddl_start`)}\n ON ddl_command_start\n WHEN TAG IN (${lit(TAGS)})\n EXECUTE PROCEDURE ${schema}.emit_ddl_start();\n\nCREATE EVENT TRIGGER ${sharded(`${appID}_ddl_end`)}\n ON ddl_command_end\n WHEN TAG IN (${lit([...TAGS, 'COMMENT'])})\n EXECUTE PROCEDURE ${schema}.emit_ddl_end();\n`,\n ];\n\n // Drop legacy functions / triggers.\n triggers.push(\n `DROP FUNCTION IF EXISTS ${schema}.emit_ddl_end(text) CASCADE;`,\n `DROP FUNCTION IF EXISTS ${schema}.notice_ignore(text, record);`,\n );\n for (const tag of [...TAGS, 'COMMENT']) {\n const tagID = tag.toLowerCase().replace(' ', '_');\n triggers.push(`DROP FUNCTION IF EXISTS ${schema}.emit_${tagID}() CASCADE;`);\n }\n return triggers.join('');\n}\n\n// Exported for testing.\nexport function dropEventTriggerStatements(\n appID: string,\n shardID: string | number,\n) {\n return /*sql*/ `\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_start_${shardID}`)};\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_end_${shardID}`)};\n `;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ddl.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"sourcesContent":["import {literal as lit} from 'pg-format';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {publishedSchema, publishedSchemaQuery} from './published.ts';\n\n// Sent in the 'version' tag of \"ddlStart\" and \"ddlUpdate\" event messages.\n// This is used to ensure that the message constructed in the upstream\n// Trigger function is compatible with the code processing it in the zero-cache.\n//\n// Increment this when changing the format of the contents of the \"ddl\" events.\n// This will allow old / incompatible code to detect the change and abort.\nexport const PROTOCOL_VERSION = 1;\n\n// In protocol v2 (planned, not yet emitted), \"ddlStart\" events that are not\n// associated with a schema change will be context-only, omitting the\n// `schema` snapshot entirely. This avoids bloating the WAL with (large)\n// redundant schema snapshots for DDL commands that do not affect the\n// published schema, e.g. the CREATE/ALTER/DROP TABLE sub-commands executed\n// by REFRESH MATERIALIZED VIEW CONCURRENTLY.\n//\n// This release already accepts v2 events so that the (subsequent) release\n// that upgrades the triggers to emit them remains rollback safe with respect\n// to this one.\nconst versionSchema = v.literalUnion(PROTOCOL_VERSION, 2);\n\nconst triggerEvent = v.object({\n context: v.object({query: v.string()}).rest(v.string()),\n});\n\nexport const ddlEventSchema = triggerEvent.extend({\n version: versionSchema,\n event: v.object({tag: v.string()}),\n // Maps the OID of each published table to the `attnum`s of published\n // columns that were created in the (upstream) transaction that emitted\n // the event. Such columns are guaranteed to have the column default in\n // all pre-existing rows, and can thus be replicated without backfill if\n // the default value itself is replicable. Tables whose publication\n // entries (e.g. column lists) were modified in the same transaction are\n // excluded, since a newly *published* (as opposed to newly *created*)\n // column may hold arbitrary values in existing rows.\n //\n // The field is absent in messages from older versions of the upstream\n // functions (in which case backfill decisions fall back to the command\n // tag heuristic), and `null` when there are no such columns.\n newColumns: v.record(v.array(v.number())).nullable().optional(),\n});\n\n/**\n * A {@link DdlStartEvent} message is emitted before every DDL event,\n * containing the command `tag`.\n *\n * In most cases, the `DdlStartEvent` itself will not be associated with a\n * schema change, in which case the event is context-only (in protocol v2,\n * planned, both `previousSchema` and `schema` will be absent; in v1,\n * `schema` is set and `previousSchema` is `null`). The message is still\n * emitted to provide the command `tag` context in case an immediately\n * following `DdlStartEvent` tag is emitted with a schema change (which can\n * happen when another event trigger results in a nested ddl statement).\n *\n * In such cases, the `previousSchema` and `schema` fields of the latter event\n * are used to determine the necessary schema change operations (as they are\n * with `ddlUpdate` and `schemaSnapshot` events), and the `tag` of the\n * preceding start event indicates the command that precipitated the schema\n * change (e.g. a CREATE vs ALTER) to determine whether a backfill is\n * necessary.\n */\nexport const ddlStartEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlStart'),\n // Set (along with `previousSchema`) only if the ddlStart event itself is\n // associated with a schema change. Absent in context-only (protocol v2,\n // planned) events. v1 events always contain the current `schema`.\n schema: publishedSchema.optional(),\n // For ddlStart messages, previousSchema is `null` (v1) or absent\n // (protocol v2, planned) if there was no change in schema detected.\n previousSchema: publishedSchema.nullable().optional(),\n // For backwards compatibility with previous versions of the trigger,\n // default an absent `event` field with a semantic equivalent. This\n // field override can be removed in a version that is rollback safe\n // with 1.4.0.\n event: v.object({tag: v.string()}).optional(() => ({tag: 'UNKNOWN'})),\n});\n\nexport type DdlStartEvent = v.Infer<typeof ddlStartEventSchema>;\n\n/**\n * A {@link DdlUpdateEvent} is emitted if there was a change in the schema.\n * It always contains `previousSchema` and (current) `schema` fields, leaving\n * it to the receiver to compute the necessary schema change operations.\n */\nexport const ddlUpdateEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlUpdate'),\n // ddlUpdate messages are only emitted if the schema changed. `schema`\n // contains the current (i.e. post-change) snapshot.\n schema: publishedSchema,\n // The `previousSchema` contains the schema before the change.\n //\n // In 1.5.0 it is always set, and can be made non-optional when\n // rollback safe.\n previousSchema: publishedSchema.optional(),\n});\n\nexport type DdlUpdateEvent = v.Infer<typeof ddlUpdateEventSchema>;\n\n/**\n * The `schemaSnapshot` message is a snapshot of a schema taken in response to\n * a `COMMENT ON PUBLICATION` command, which is a hook recognized by zero\n * to manually emit `previousSchema` and `schema` snapshots when a difference\n * is detected. This is a workaround provided to support detection of schema\n * changes from `ALTER PUBLICATION` commands on supabase, which does not fire\n * event triggers for them (https://github.com/supabase/supautils/issues/123).\n *\n * The hook is exercised by trailing the publication change with a\n * `COMMENT ON PUBLICATION` statement, e.g.\n *\n * ```sql\n * BEGIN;\n * ALTER PUBLICATION my_publication ...;\n * COMMENT ON PUBLICATION my_publication IS 'whatever';\n * COMMIT;\n * ```\n *\n * Note that it is fine to invoke `COMMENT ON PUBLICATION` statements\n * on a database that *does* support event triggers on\n * `ALTER PUBLICATION` statements, as it will simply be a no-op.\n */\nexport const schemaSnapshotEventSchema = ddlEventSchema.extend({\n type: v.literal('schemaSnapshot'),\n schema: publishedSchema,\n previousSchema: publishedSchema.optional(),\n});\n\nexport type SchemaSnapshotEvent = v.Infer<typeof schemaSnapshotEventSchema>;\n\nexport const replicationEventSchema = v.union(\n ddlStartEventSchema,\n ddlUpdateEventSchema,\n schemaSnapshotEventSchema,\n);\n\nexport type ReplicationEvent = v.Infer<typeof replicationEventSchema>;\n\n// Creates a function that appends `_{shard-num}` to the input and\n// quotes the result to be a valid identifier.\nfunction append(shardNum: number) {\n return (name: string) => id(name + '_' + String(shardNum));\n}\n\n// pg_advisory_xact_lock key for serializing ddl statements in order to\n// produce correct schema change diffs.\nconst DDL_SERIALIZATION_LOCK = 0x3c6b8468f1bac0b0n;\n\n/**\n * Event trigger functions contain the core logic that are invoked by triggers.\n *\n * Note that although many of these functions can theoretically be parameterized and\n * shared across shards, it is advantageous to keep the functions in each shard\n * isolated from each other in order to avoid the complexity of shared-function\n * versioning.\n *\n * In a sense, shards (and their triggers and functions) should be thought of as\n * execution environments that can be updated at different schedules. If per-shard\n * triggers called into shared functions, we would have to consider versioning the\n * functions when changing their behavior, backwards compatibility, removal of\n * unused versions, etc. (not unlike versioning of npm packages).\n *\n * Instead, we opt for the simplicity and isolation of having each shard\n * completely own (and maintain) the entirety of its trigger/function stack.\n */\nexport function createEventFunctionStatements(shard: ShardConfig) {\n const {appID, shardNum, publications} = shard;\n const schema = id(upstreamSchema(shard)); // e.g. \"{APP_ID}_{SHARD_ID}\"\n return /*sql*/ `\nCREATE SCHEMA IF NOT EXISTS ${schema};\n\nCREATE OR REPLACE FUNCTION ${schema}.get_trigger_context()\nRETURNS record AS $$\nDECLARE\n result record;\nBEGIN\n SELECT COALESCE(current_query(), 'current_query() returned NULL') AS \"query\" into result;\n RETURN result;\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.notice_ignore(reason TEXT, tag TEXT, target record)\nRETURNS void AS $$\nBEGIN\n RAISE NOTICE '${appID}_${shardNum} ignoring % % %', reason, tag, \n COALESCE(row_to_json(target)::text, '');\nEND\n$$ LANGUAGE plpgsql;\n\n\n-- Note: DROP and CREATE to upgrade from v20 to v21 because the\n-- return type has changed. This can be simplified to CREATE OR REPLACE\n-- once 1.5.0 is rollback safe.\nDROP FUNCTION IF EXISTS ${schema}.schema_specs();\nCREATE FUNCTION ${schema}.schema_specs()\nRETURNS JSON \nSTABLE\nAS $$\n ${publishedSchemaQuery(publications)}\n$$ LANGUAGE sql;\n\n\n-- Stores the most recent published schema\nCREATE TABLE IF NOT EXISTS ${schema}.\"publishedSchema\" (\n current JSON,\n exists BOOL PRIMARY KEY DEFAULT true CHECK (exists)\n);\n\nINSERT INTO ${schema}.\"publishedSchema\" (current) VALUES (${schema}.schema_specs())\n ON CONFLICT (exists) DO \n UPDATE SET current = excluded.current;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.update_schemas(event_type text, tag text, target record)\nRETURNS void AS $$\nDECLARE\n prev_schema_specs JSON;\n schema_specs JSON;\n message TEXT;\nBEGIN\n SELECT current FROM ${schema}.\"publishedSchema\" INTO prev_schema_specs;\n SELECT ${schema}.schema_specs() INTO schema_specs;\n \n IF prev_schema_specs::text != schema_specs::text THEN\n UPDATE ${schema}.\"publishedSchema\" SET current = schema_specs;\n ELSIF event_type = 'ddlStart' THEN\n -- ddlStart events are always be emitted to allow the zero-cache\n -- to track the context of the current command tag in the face of\n -- nested event triggers (e.g. start->start->end->end).\n prev_schema_specs = NULL;\n ELSIF event_type = 'ddlUpdate' THEN\n -- TODO: fold 'schemaSnapshot' into this condition too (i.e. make it \"ELSE\")\n -- when 1.5.0 is rollback safe. Until then, noop schemaSnapshots are sent\n -- for compatibility with 1.0.0 ~ 1.4.0.\n PERFORM ${schema}.notice_ignore('noop', tag, target);\n RETURN;\n END IF;\n\n SELECT json_build_object(\n 'type', event_type,\n 'version', ${PROTOCOL_VERSION},\n 'previousSchema', prev_schema_specs,\n 'schema', schema_specs,\n 'event', json_build_object('tag', tag),\n 'context', ${schema}.get_trigger_context()\n ) INTO message;\n\n PERFORM pg_logical_emit_message(true, '${appID}/${shardNum}/ddl', message);\n\n RAISE NOTICE 'Emitted ${appID}_${shardNum} % for % %', event_type, tag,\n COALESCE(row_to_json(target)::text, '');\nEND\n$$ LANGUAGE plpgsql;\n\n\n-- Hook/workaround to manually trigger replication of schema changes on DBs \n-- that do not support/allow event triggers.\nCREATE OR REPLACE FUNCTION ${schema}.update_schemas()\nRETURNS void AS $$\nBEGIN\n PERFORM ${schema}.update_schemas('schemaSnapshot', 'MANUAL', NULL);\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_start()\nRETURNS event_trigger AS $$\nDECLARE\n schema_specs JSON;\n message TEXT;\nBEGIN\n -- serialize DDL statements to compute correct schema change diffs\n PERFORM pg_advisory_xact_lock(${DDL_SERIALIZATION_LOCK});\n PERFORM ${schema}.update_schemas('ddlStart', TG_TAG, NULL);\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_end()\nRETURNS event_trigger AS $$\nDECLARE\n publications TEXT[];\n target RECORD;\n relevant RECORD;\n schema_specs JSON;\n message TEXT;\n event TEXT;\nBEGIN\n publications := ARRAY[${lit(publications)}];\n\n SELECT objid, object_type, object_identity \n FROM pg_event_trigger_ddl_commands() \n LIMIT 1 INTO target;\n\n -- Filter DDL updates that are not relevant to the shard (i.e. publications) when possible.\n SELECT true INTO relevant;\n\n -- Note: ALTER TABLE statements may *remove* the table from the set of published\n -- tables, and there is no way to determine if the table \"used to be\" in the\n -- set. Thus, all ALTER TABLE statements must produce a ddl update, similar to\n -- any DROP * statement.\n IF (target.object_type = 'table' AND TG_TAG != 'ALTER TABLE') \n OR target.object_type = 'table column' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'index' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_indexes as ind ON ind.schemaname = ns.nspname AND ind.indexname = c.relname\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = ind.tablename\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication relation' THEN\n SELECT pb.pubname FROM pg_publication_rel AS rel\n JOIN pg_publication AS pb ON pb.oid = rel.prpubid\n WHERE rel.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'publication namespace' THEN\n SELECT pb.pubname FROM pg_publication_namespace AS ns\n JOIN pg_publication AS pb ON pb.oid = ns.pnpubid\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'schema' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication' THEN\n SELECT 1 WHERE target.object_identity = ANY (publications)\n INTO relevant;\n\n -- no-op CREATE IF NOT EXIST statements\n ELSIF TG_TAG LIKE 'CREATE %' AND target.object_type IS NULL THEN\n relevant := NULL;\n END IF;\n\n IF relevant IS NULL THEN\n PERFORM ${schema}.notice_ignore('irrelevant', TG_TAG, target);\n RETURN;\n END IF;\n\n IF TG_TAG = 'COMMENT' THEN\n -- Only make schemaSnapshots for COMMENT ON PUBLICATION\n IF target.object_type != 'publication' THEN\n PERFORM ${schema}.notice_ignore('irrelevant', TG_TAG, target);\n RETURN;\n END IF;\n PERFORM ${schema}.update_schemas('schemaSnapshot', TG_TAG, target);\n ELSE\n PERFORM ${schema}.update_schemas('ddlUpdate', TG_TAG, target);\n END IF;\n\nEND\n$$ LANGUAGE plpgsql;\n`;\n}\n\n// Exported for testing.\nexport const TAGS = [\n 'CREATE TABLE',\n 'ALTER TABLE',\n 'CREATE INDEX',\n 'DROP TABLE',\n 'DROP INDEX',\n 'ALTER PUBLICATION',\n 'ALTER SCHEMA',\n] as const;\n\nexport function createEventTriggerStatements(shard: ShardConfig) {\n // Better to assert here than get a cryptic syntax error from Postgres.\n assert(shard.publications.length, `shard publications must be non-empty`);\n\n // Unlike functions, which are namespaced in shard-specific schemas,\n // EVENT TRIGGER names are in the global namespace and thus must include\n // the appID and shardNum.\n const {appID, shardNum} = shard;\n const sharded = append(shardNum);\n const schema = id(upstreamSchema(shard));\n\n const triggers = [\n dropEventTriggerStatements(shard.appID, shard.shardNum),\n /*sql*/ `\nCREATE EVENT TRIGGER ${sharded(`${appID}_ddl_start`)}\n ON ddl_command_start\n WHEN TAG IN (${lit(TAGS)})\n EXECUTE PROCEDURE ${schema}.emit_ddl_start();\n\nCREATE EVENT TRIGGER ${sharded(`${appID}_ddl_end`)}\n ON ddl_command_end\n WHEN TAG IN (${lit([...TAGS, 'COMMENT'])})\n EXECUTE PROCEDURE ${schema}.emit_ddl_end();\n`,\n ];\n\n // Drop legacy functions / triggers.\n triggers.push(\n `DROP FUNCTION IF EXISTS ${schema}.emit_ddl_end(text) CASCADE;`,\n `DROP FUNCTION IF EXISTS ${schema}.notice_ignore(text, record);`,\n );\n for (const tag of [...TAGS, 'COMMENT']) {\n const tagID = tag.toLowerCase().replace(' ', '_');\n triggers.push(`DROP FUNCTION IF EXISTS ${schema}.emit_${tagID}() CASCADE;`);\n }\n return triggers.join('');\n}\n\n// Exported for testing.\nexport function dropEventTriggerStatements(\n appID: string,\n shardID: string | number,\n) {\n return /*sql*/ `\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_start_${shardID}`)};\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_end_${shardID}`)};\n `;\n}\n"],"mappings":"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"}
@@ -4,6 +4,6 @@ import { type ShardConfig } from '../../../../types/shards.ts';
4
4
  /**
5
5
  * Ensures that a shard is set up for initial sync.
6
6
  */
7
- export declare function ensureShardSchema(lc: LogContext, db: PostgresDB, shard: ShardConfig, installPartialIndexTriggers?: boolean): Promise<void>;
7
+ export declare function ensureShardSchema(lc: LogContext, db: PostgresDB, shard: ShardConfig): Promise<void>;
8
8
  export declare const CURRENT_SCHEMA_VERSION: number;
9
9
  //# sourceMappingURL=init.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAQjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAiB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAY7E;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,EAClB,2BAA2B,UAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CAiBf;AAgOD,eAAO,MAAM,sBAAsB,QASwB,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAQjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAiB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAY7E;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAgBf;AA4MD,eAAO,MAAM,sBAAsB,QAMwB,CAAC"}
@@ -10,13 +10,13 @@ import { runSchemaMigrations } from "../../../../db/migration.js";
10
10
  /**
11
11
  * Ensures that a shard is set up for initial sync.
12
12
  */
13
- async function ensureShardSchema(lc, db, shard, installPartialIndexTriggers = true) {
13
+ async function ensureShardSchema(lc, db, shard) {
14
14
  await runSchemaMigrations(lc, `upstream-shard-${shard.appID}`, upstreamSchema(shard), db, {
15
- migrateSchema: (lc, tx) => setupTablesAndReplication(lc, tx, shard, installPartialIndexTriggers),
15
+ migrateSchema: (lc, tx) => setupTablesAndReplication(lc, tx, shard),
16
16
  minSafeVersion: 1
17
- }, getIncrementalMigrations(shard, installPartialIndexTriggers));
17
+ }, getIncrementalMigrations(shard));
18
18
  }
19
- function getIncrementalMigrations(shard, installPartialIndexTriggers) {
19
+ function getIncrementalMigrations(shard) {
20
20
  const shardConfigTable = `${upstreamSchema(shard)}.shardConfig`;
21
21
  return {
22
22
  6: { migrateSchema: () => {
@@ -129,23 +129,14 @@ function getIncrementalMigrations(shard, installPartialIndexTriggers) {
129
129
  ALTER TABLE ${sql(upstreamSchema(shard))}.replicas
130
130
  ADD COLUMN "backupV5" BOOL DEFAULT false;
131
131
  `;
132
- } },
133
- ...installPartialIndexTriggers ? { 26: { migrateSchema: async (lc, sql) => {
134
- const [{ publications }] = await sql`
135
- SELECT publications FROM ${sql(shardConfigTable)}`;
136
- await setupTriggers(lc, sql, {
137
- ...shard,
138
- publications
139
- });
140
- lc.info?.(`Upgraded DDL event triggers`);
141
- } } } : {}
132
+ } }
142
133
  };
143
134
  }
144
135
  Object.keys(getIncrementalMigrations({
145
136
  appID: "unused",
146
137
  shardNum: 0,
147
138
  publications: ["foo"]
148
- }, true)).reduce((prev, curr) => Math.max(prev, parseInt(curr)), 0);
139
+ })).reduce((prev, curr) => Math.max(prev, parseInt(curr)), 0);
149
140
  //#endregion
150
141
  export { ensureShardSchema };
151
142
 
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../../db/migration.ts';\nimport type {PostgresDB} from '../../../../types/pg.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {AutoResetSignal} from '../../../change-streamer/schema/tables.ts';\nimport {publishedSchema} from './published.ts';\nimport {\n getMutationsTableDefinition,\n legacyReplicationSlot,\n metadataPublicationName,\n setupTablesAndReplication,\n setupTriggers,\n} from './shard.ts';\n\n/**\n * Ensures that a shard is set up for initial sync.\n */\nexport async function ensureShardSchema(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n installPartialIndexTriggers = true,\n): Promise<void> {\n const initialSetup: Migration = {\n migrateSchema: (lc, tx) =>\n setupTablesAndReplication(lc, tx, shard, installPartialIndexTriggers),\n minSafeVersion: 1,\n };\n await runSchemaMigrations(\n lc,\n `upstream-shard-${shard.appID}`,\n upstreamSchema(shard),\n db,\n initialSetup,\n // The incremental migration of any existing replicas will be replaced by\n // the incoming replica being synced, so the replicaVersion here is\n // unnecessary.\n getIncrementalMigrations(shard, installPartialIndexTriggers),\n );\n}\n\nfunction getIncrementalMigrations(\n shard: ShardConfig,\n installPartialIndexTriggers: boolean,\n): IncrementalMigrationMap {\n const shardConfigTable = `${upstreamSchema(shard)}.shardConfig`;\n\n return {\n 6: {\n migrateSchema: () => {\n throw new AutoResetSignal('resetting to upgrade shard schema');\n },\n },\n\n // v7: Updates the DDL event trigger protocol to v2, and adds support for\n // ALTER SCHEMA x RENAME TO y\n // (migration logic subsumbed by v23)\n\n // v8: Adds support for non-disruptive resyncs, which tracks multiple\n // replicas with different slot names.\n 8: {\n migrateSchema: async (lc, sql) => {\n const legacyShardConfigSchema = v.object({\n replicaVersion: v.string().nullable(),\n initialSchema: publishedSchema.nullable(),\n });\n const result = await sql`\n SELECT \"replicaVersion\", \"initialSchema\" FROM ${sql(shardConfigTable)}`;\n assert(\n result.length === 1,\n () => `Expected exactly one shardConfig row, got ${result.length}`,\n );\n const {replicaVersion, initialSchema} = v.parse(\n result[0],\n legacyShardConfigSchema,\n 'passthrough',\n );\n\n await Promise.all([\n sql`\n CREATE TABLE ${sql(upstreamSchema(shard))}.replicas (\n \"slot\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL,\n \"initialSchema\" JSON NOT NULL\n );\n `,\n sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.replicas ${sql({\n slot: legacyReplicationSlot(shard),\n version: replicaVersion,\n initialSchema,\n })}\n `,\n sql`\n ALTER TABLE ${sql(shardConfigTable)} DROP \"replicaVersion\", DROP \"initialSchema\"\n `,\n ]);\n lc.info?.(`Upgraded schema to support non-disruptive resyncs`);\n },\n },\n\n // v9: Fixes field ordering of compound indexes. This incremental migration\n // only fixes indexes resulting from new schema changes. A full resync is\n // required to fix existing indexes.\n //\n // The migration has been subsumed by the identical logic for migrating\n // to v12 (i.e. a trigger upgrade).\n\n // Adds the `mutations` table used to track mutation results.\n 10: {\n migrateSchema: async (lc, sql) => {\n await sql.unsafe(/*sql*/ `\n ${getMutationsTableDefinition(upstreamSchema(shard))}\n ALTER PUBLICATION ${id(metadataPublicationName(shard.appID, shard.shardNum))} ADD TABLE ${id(upstreamSchema(shard))}.\"mutations\";\n `);\n lc.info?.('Upgraded schema with new mutations table');\n },\n },\n\n // v11: Formerly dropped the schemaVersions table, but restored in the v13\n // migration for rollback safety.\n\n // v12: Upgrade DDL trigger to query schemaOID, needed information for auto-backfill.\n // (subsumed by v14)\n\n // Recreates the legacy schemaVersions table that was prematurely dropped\n // in the (former) v11 migration. It needs to remain present for at least one\n // release in order to be rollback safe.\n //\n // TODO: Drop the table once a release that no longer reads the table has\n // been rolled out.\n 13: {\n migrateSchema: async (_, sql) => {\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql(upstreamSchema(shard))}.\"schemaVersions\" (\n \"minSupportedVersion\" INT4,\n \"maxSupportedVersion\" INT4,\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );`;\n await sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.\"schemaVersions\" \n (\"lock\", \"minSupportedVersion\", \"maxSupportedVersion\")\n VALUES (true, 1, 1)\n ON CONFLICT DO NOTHING;\n `;\n },\n },\n\n // v14: Upgrade DDL trigger to log more info to PG logs.\n // (subsumed by v16)\n\n // Add initialSyncContext column to replicas table.\n 15: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas\n ADD COLUMN \"initialSyncContext\" JSON,\n ADD COLUMN \"subscriberContext\" JSON\n `;\n },\n },\n\n // v16: Upgrade DDL trigger to fire on all ALTER TABLE statements\n // to catch the *removal* of a table from the published set.\n // v17 (1.0.0): Upgrade DDL triggers to support the COMMENT ON PUBLICATION hook for\n // working around the lack of event trigger support for PUBLICATION\n // changes in supabase.\n // This also adds forwards-compatible support for hierarchical logical\n // message prefixes and unknown ddl event types.\n // v18: Pure refactoring of event trigger code.\n // v19 (1.4.0): Correctly handle concurrently issued DDL statements.\n // v20 (1.4.0): Handle nested DDL triggers\n // v21 (1.5.0): Handle cross-transaction DDL operations (i.e. concurrent\n // index operations), and support manual invocation of update_schemas().\n\n 22: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ALTER \"initialSchema\" DROP NOT NULL;\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n DROP CONSTRAINT replicas_pkey;\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN id TEXT PRIMARY KEY DEFAULT replace(gen_random_uuid()::text, '-', '');\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN rank BIGSERIAL;\n `;\n },\n },\n\n // v23 (1.6.0): Event triggers: Handle the case where current_query()\n // returns NULL. Add more logging to debug unexpected messages\n 23: {\n migrateSchema: async (lc, sql) => {\n const [{publications}] = await sql<{publications: string[]}[]>`\n SELECT publications FROM ${sql(shardConfigTable)}`;\n await setupTriggers(lc, sql, {...shard, publications});\n lc.info?.(`Upgraded DDL event triggers`);\n },\n },\n\n // v24 (1.10.0):\n // - adds the \"backupPath\" column for per-replica backups.\n // - adds the \"generation\" column to replace \"version\".\n 24: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN \"backupPath\" TEXT;\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN \"generation\" TEXT;\n `;\n },\n\n migrateData: async (_, sql) => {\n // Backfill the \"generation\" column to eventually replace \"version\"\n await sql`\n UPDATE ${sql(upstreamSchema(shard))}.replicas SET \"generation\" = \"version\";\n `;\n },\n },\n\n // v25 (1.10.0):\n // - adds the \"backupV5\" column to signal the current semantics\n // of the replication slot's confirmed_flush_lsn with respect to what\n // has been backed up.\n 25: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN \"backupV5\" BOOL DEFAULT false;\n `;\n },\n },\n\n ...(installPartialIndexTriggers\n ? {\n // v26: Upgrade the DDL event triggers to include partial indexes in\n // schema snapshots. Note that setupTriggers() also refreshes the\n // stored \"publishedSchema\" so that the change in format does not\n // manifest as a spurious schema change.\n 26: {\n migrateSchema: async (lc, sql) => {\n const [{publications}] = await sql<{publications: string[]}[]>`\n SELECT publications FROM ${sql(shardConfigTable)}`;\n await setupTriggers(lc, sql, {...shard, publications});\n lc.info?.(`Upgraded DDL event triggers`);\n },\n },\n }\n : {}),\n };\n}\n\n// Referenced in tests.\nexport const CURRENT_SCHEMA_VERSION = Object.keys(\n getIncrementalMigrations(\n {\n appID: 'unused',\n shardNum: 0,\n publications: ['foo'],\n },\n true,\n ),\n).reduce((prev, curr) => Math.max(prev, parseInt(curr)), 0);\n"],"mappings":";;;;;;;;;;;;AAwBA,eAAsB,kBACpB,IACA,IACA,OACA,8BAA8B,MACf;CAMf,MAAM,oBACJ,IACA,kBAAkB,MAAM,SACxB,eAAe,KAAK,GACpB,IACA;EATA,gBAAgB,IAAI,OAClB,0BAA0B,IAAI,IAAI,OAAO,2BAA2B;EACtE,gBAAgB;CAOhB,GAIA,yBAAyB,OAAO,2BAA2B,CAC7D;AACF;AAEA,SAAS,yBACP,OACA,6BACyB;CACzB,MAAM,mBAAmB,GAAG,eAAe,KAAK,EAAE;CAElD,OAAO;EACL,GAAG,EACD,qBAAqB;GACnB,MAAM,IAAI,gBAAgB,mCAAmC;EAC/D,EACF;EAQA,GAAG,EACD,eAAe,OAAO,IAAI,QAAQ;GAChC,MAAM,0BAA0B,eAAE,OAAO;IACvC,gBAAgB,eAAE,OAAO,EAAE,SAAS;IACpC,eAAe,gBAAgB,SAAS;GAC1C,CAAC;GACD,MAAM,SAAS,MAAM,GAAG;0DAC0B,IAAI,gBAAgB;GACtE,OACE,OAAO,WAAW,SACZ,6CAA6C,OAAO,QAC5D;GACA,MAAM,EAAC,gBAAgB,kBAAiB,MACtC,OAAO,IACP,yBACA,aACF;GAEA,MAAM,QAAQ,IAAI;IAChB,GAAG;yBACY,IAAI,eAAe,KAAK,CAAC,EAAE;;;;;;IAM1C,GAAG;wBACW,IAAI,eAAe,KAAK,CAAC,EAAE,YAAY,IAAI;KACvD,MAAM,sBAAsB,KAAK;KACjC,SAAS;KACT;IACF,CAAC,EAAE;;IAEH,GAAG;wBACW,IAAI,gBAAgB,EAAE;;GAEtC,CAAC;GACD,GAAG,OAAO,mDAAmD;EAC/D,EACF;EAUA,IAAI,EACF,eAAe,OAAO,IAAI,QAAQ;GAChC,MAAM,IAAI,OAAe;YACrB,4BAA4B,eAAe,KAAK,CAAC,EAAE;8BACjC,GAAG,wBAAwB,MAAM,OAAO,MAAM,QAAQ,CAAC,EAAE,aAAa,GAAG,eAAe,KAAK,CAAC,EAAE;SACrH;GACD,GAAG,OAAO,0CAA0C;EACtD,EACF;EAcA,IAAI,EACF,eAAe,OAAO,GAAG,QAAQ;GAC/B,MAAM,GAAG;uCACsB,IAAI,eAAe,KAAK,CAAC,EAAE;;;;;GAK1D,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;;;EAK7C,EACF;EAMA,IAAI,EACF,eAAe,OAAO,GAAG,QAAQ;GAC/B,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;;EAI7C,EACF;EAeA,IAAI,EACF,eAAe,OAAO,GAAG,QAAQ;GAC/B,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;GAG3C,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;GAG3C,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;GAG3C,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;EAG7C,EACF;EAIA,IAAI,EACF,eAAe,OAAO,IAAI,QAAQ;GAChC,MAAM,CAAC,EAAC,kBAAiB,MAAM,GAA+B;qCACjC,IAAI,gBAAgB;GACjD,MAAM,cAAc,IAAI,KAAK;IAAC,GAAG;IAAO;GAAY,CAAC;GACrD,GAAG,OAAO,6BAA6B;EACzC,EACF;EAKA,IAAI;GACF,eAAe,OAAO,GAAG,QAAQ;IAC/B,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;IAG3C,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;GAG7C;GAEA,aAAa,OAAO,GAAG,QAAQ;IAE7B,MAAM,GAAG;mBACE,IAAI,eAAe,KAAK,CAAC,EAAE;;GAExC;EACF;EAMA,IAAI,EACF,eAAe,OAAO,GAAG,QAAQ;GAC/B,MAAM,GAAG;wBACO,IAAI,eAAe,KAAK,CAAC,EAAE;;;EAG7C,EACF;EAEA,GAAI,8BACA,EAKE,IAAI,EACF,eAAe,OAAO,IAAI,QAAQ;GAChC,MAAM,CAAC,EAAC,kBAAiB,MAAM,GAA+B;2CACjC,IAAI,gBAAgB;GACjD,MAAM,cAAc,IAAI,KAAK;IAAC,GAAG;IAAO;GAAY,CAAC;GACrD,GAAG,OAAO,6BAA6B;EACzC,EACF,EACF,IACA,CAAC;CACP;AACF;AAGsC,OAAO,KAC3C,yBACE;CACE,OAAO;CACP,UAAU;CACV,cAAc,CAAC,KAAK;AACtB,GACA,IACF,CACF,EAAE,QAAQ,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,IAAI,CAAC,GAAG,CAAC"}
1
+ {"version":3,"file":"init.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../../db/migration.ts';\nimport type {PostgresDB} from '../../../../types/pg.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {AutoResetSignal} from '../../../change-streamer/schema/tables.ts';\nimport {publishedSchema} from './published.ts';\nimport {\n getMutationsTableDefinition,\n legacyReplicationSlot,\n metadataPublicationName,\n setupTablesAndReplication,\n setupTriggers,\n} from './shard.ts';\n\n/**\n * Ensures that a shard is set up for initial sync.\n */\nexport async function ensureShardSchema(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n): Promise<void> {\n const initialSetup: Migration = {\n migrateSchema: (lc, tx) => setupTablesAndReplication(lc, tx, shard),\n minSafeVersion: 1,\n };\n await runSchemaMigrations(\n lc,\n `upstream-shard-${shard.appID}`,\n upstreamSchema(shard),\n db,\n initialSetup,\n // The incremental migration of any existing replicas will be replaced by\n // the incoming replica being synced, so the replicaVersion here is\n // unnecessary.\n getIncrementalMigrations(shard),\n );\n}\n\nfunction getIncrementalMigrations(shard: ShardConfig): IncrementalMigrationMap {\n const shardConfigTable = `${upstreamSchema(shard)}.shardConfig`;\n\n return {\n 6: {\n migrateSchema: () => {\n throw new AutoResetSignal('resetting to upgrade shard schema');\n },\n },\n\n // v7: Updates the DDL event trigger protocol to v2, and adds support for\n // ALTER SCHEMA x RENAME TO y\n // (migration logic subsumbed by v23)\n\n // v8: Adds support for non-disruptive resyncs, which tracks multiple\n // replicas with different slot names.\n 8: {\n migrateSchema: async (lc, sql) => {\n const legacyShardConfigSchema = v.object({\n replicaVersion: v.string().nullable(),\n initialSchema: publishedSchema.nullable(),\n });\n const result = await sql`\n SELECT \"replicaVersion\", \"initialSchema\" FROM ${sql(shardConfigTable)}`;\n assert(\n result.length === 1,\n () => `Expected exactly one shardConfig row, got ${result.length}`,\n );\n const {replicaVersion, initialSchema} = v.parse(\n result[0],\n legacyShardConfigSchema,\n 'passthrough',\n );\n\n await Promise.all([\n sql`\n CREATE TABLE ${sql(upstreamSchema(shard))}.replicas (\n \"slot\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL,\n \"initialSchema\" JSON NOT NULL\n );\n `,\n sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.replicas ${sql({\n slot: legacyReplicationSlot(shard),\n version: replicaVersion,\n initialSchema,\n })}\n `,\n sql`\n ALTER TABLE ${sql(shardConfigTable)} DROP \"replicaVersion\", DROP \"initialSchema\"\n `,\n ]);\n lc.info?.(`Upgraded schema to support non-disruptive resyncs`);\n },\n },\n\n // v9: Fixes field ordering of compound indexes. This incremental migration\n // only fixes indexes resulting from new schema changes. A full resync is\n // required to fix existing indexes.\n //\n // The migration has been subsumed by the identical logic for migrating\n // to v12 (i.e. a trigger upgrade).\n\n // Adds the `mutations` table used to track mutation results.\n 10: {\n migrateSchema: async (lc, sql) => {\n await sql.unsafe(/*sql*/ `\n ${getMutationsTableDefinition(upstreamSchema(shard))}\n ALTER PUBLICATION ${id(metadataPublicationName(shard.appID, shard.shardNum))} ADD TABLE ${id(upstreamSchema(shard))}.\"mutations\";\n `);\n lc.info?.('Upgraded schema with new mutations table');\n },\n },\n\n // v11: Formerly dropped the schemaVersions table, but restored in the v13\n // migration for rollback safety.\n\n // v12: Upgrade DDL trigger to query schemaOID, needed information for auto-backfill.\n // (subsumed by v14)\n\n // Recreates the legacy schemaVersions table that was prematurely dropped\n // in the (former) v11 migration. It needs to remain present for at least one\n // release in order to be rollback safe.\n //\n // TODO: Drop the table once a release that no longer reads the table has\n // been rolled out.\n 13: {\n migrateSchema: async (_, sql) => {\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql(upstreamSchema(shard))}.\"schemaVersions\" (\n \"minSupportedVersion\" INT4,\n \"maxSupportedVersion\" INT4,\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );`;\n await sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.\"schemaVersions\" \n (\"lock\", \"minSupportedVersion\", \"maxSupportedVersion\")\n VALUES (true, 1, 1)\n ON CONFLICT DO NOTHING;\n `;\n },\n },\n\n // v14: Upgrade DDL trigger to log more info to PG logs.\n // (subsumed by v16)\n\n // Add initialSyncContext column to replicas table.\n 15: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas\n ADD COLUMN \"initialSyncContext\" JSON,\n ADD COLUMN \"subscriberContext\" JSON\n `;\n },\n },\n\n // v16: Upgrade DDL trigger to fire on all ALTER TABLE statements\n // to catch the *removal* of a table from the published set.\n // v17 (1.0.0): Upgrade DDL triggers to support the COMMENT ON PUBLICATION hook for\n // working around the lack of event trigger support for PUBLICATION\n // changes in supabase.\n // This also adds forwards-compatible support for hierarchical logical\n // message prefixes and unknown ddl event types.\n // v18: Pure refactoring of event trigger code.\n // v19 (1.4.0): Correctly handle concurrently issued DDL statements.\n // v20 (1.4.0): Handle nested DDL triggers\n // v21 (1.5.0): Handle cross-transaction DDL operations (i.e. concurrent\n // index operations), and support manual invocation of update_schemas().\n\n 22: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ALTER \"initialSchema\" DROP NOT NULL;\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n DROP CONSTRAINT replicas_pkey;\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN id TEXT PRIMARY KEY DEFAULT replace(gen_random_uuid()::text, '-', '');\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN rank BIGSERIAL;\n `;\n },\n },\n\n // v23 (1.6.0): Event triggers: Handle the case where current_query()\n // returns NULL. Add more logging to debug unexpected messages\n 23: {\n migrateSchema: async (lc, sql) => {\n const [{publications}] = await sql<{publications: string[]}[]>`\n SELECT publications FROM ${sql(shardConfigTable)}`;\n await setupTriggers(lc, sql, {...shard, publications});\n lc.info?.(`Upgraded DDL event triggers`);\n },\n },\n\n // v24 (1.10.0):\n // - adds the \"backupPath\" column for per-replica backups.\n // - adds the \"generation\" column to replace \"version\".\n 24: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN \"backupPath\" TEXT;\n `;\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN \"generation\" TEXT;\n `;\n },\n\n migrateData: async (_, sql) => {\n // Backfill the \"generation\" column to eventually replace \"version\"\n await sql`\n UPDATE ${sql(upstreamSchema(shard))}.replicas SET \"generation\" = \"version\";\n `;\n },\n },\n\n // v25 (1.10.0):\n // - adds the \"backupV5\" column to signal the current semantics\n // of the replication slot's confirmed_flush_lsn with respect to what\n // has been backed up.\n 25: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas \n ADD COLUMN \"backupV5\" BOOL DEFAULT false;\n `;\n },\n },\n };\n}\n\n// Referenced in tests.\nexport const CURRENT_SCHEMA_VERSION = Object.keys(\n getIncrementalMigrations({\n appID: 'unused',\n shardNum: 0,\n publications: ['foo'],\n }),\n).reduce((prev, curr) => Math.max(prev, parseInt(curr)), 0);\n"],"mappings":"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"}
@@ -1,155 +1,38 @@
1
- import type { LogContext } from '@rocicorp/logger';
2
1
  import type postgres from 'postgres';
3
2
  import * as v from '../../../../../../shared/src/valita.ts';
4
- import { type UnsupportedPredicateReason } from './index-predicate.ts';
5
- export declare function publishedSchemaQuery(publications: readonly string[], includePartialIndexes?: boolean): string;
6
- export type SkippedIndexDiagnostic = {
7
- readonly schema: string;
8
- readonly index: string;
9
- readonly predicate: string;
10
- readonly reason: UnsupportedPredicateReason;
11
- };
12
- export declare function getSkippedIndexDiagnostics(schema: PublishedSchema): readonly SkippedIndexDiagnostic[];
13
- export declare function warnForSkippedIndexes(lc: LogContext, schema: PublishedSchema, warned?: Set<string>): void;
3
+ export declare function publishedSchemaQuery(publications: readonly string[]): string;
14
4
  export declare const publishedSchema: v.Type<{
15
5
  indexes: {
16
- readonly predicate?: {
17
- readonly type: "and" | "or";
18
- readonly conditions: readonly (/*elided*/ any | {
19
- readonly type: "not";
20
- readonly condition: /*elided*/ any | /*elided*/ any | {
21
- readonly type: "null-test";
22
- readonly column: string;
23
- readonly op: "IS NULL" | "IS NOT NULL";
24
- } | {
25
- readonly type: "comparison";
26
- readonly column: string;
27
- readonly op: "=" | "<>" | "<" | "<=" | ">" | ">=";
28
- readonly value: {
29
- readonly type: "boolean";
30
- readonly value: boolean;
31
- } | {
32
- readonly type: "integer";
33
- readonly value: string;
34
- } | {
35
- readonly type: "string";
36
- readonly value: string;
37
- };
38
- };
39
- } | {
40
- readonly type: "null-test";
41
- readonly column: string;
42
- readonly op: "IS NULL" | "IS NOT NULL";
43
- } | {
44
- readonly type: "comparison";
45
- readonly column: string;
46
- readonly op: "=" | "<>" | "<" | "<=" | ">" | ">=";
47
- readonly value: {
48
- readonly type: "boolean";
49
- readonly value: boolean;
50
- } | {
51
- readonly type: "integer";
52
- readonly value: string;
53
- } | {
54
- readonly type: "string";
55
- readonly value: string;
56
- };
57
- })[];
58
- } | {
59
- readonly type: "not";
60
- readonly condition: {
61
- readonly type: "and" | "or";
62
- readonly conditions: readonly (/*elided*/ any | /*elided*/ any | {
63
- readonly type: "null-test";
64
- readonly column: string;
65
- readonly op: "IS NULL" | "IS NOT NULL";
66
- } | {
67
- readonly type: "comparison";
68
- readonly column: string;
69
- readonly op: "=" | "<>" | "<" | "<=" | ">" | ">=";
70
- readonly value: {
71
- readonly type: "boolean";
72
- readonly value: boolean;
73
- } | {
74
- readonly type: "integer";
75
- readonly value: string;
76
- } | {
77
- readonly type: "string";
78
- readonly value: string;
79
- };
80
- })[];
81
- } | /*elided*/ any | {
82
- readonly type: "null-test";
83
- readonly column: string;
84
- readonly op: "IS NULL" | "IS NOT NULL";
85
- } | {
86
- readonly type: "comparison";
87
- readonly column: string;
88
- readonly op: "=" | "<>" | "<" | "<=" | ">" | ">=";
89
- readonly value: {
90
- readonly type: "boolean";
91
- readonly value: boolean;
92
- } | {
93
- readonly type: "integer";
94
- readonly value: string;
95
- } | {
96
- readonly type: "string";
97
- readonly value: string;
98
- };
99
- };
100
- } | {
101
- readonly type: "null-test";
102
- readonly column: string;
103
- readonly op: "IS NULL" | "IS NOT NULL";
104
- } | {
105
- readonly type: "comparison";
106
- readonly column: string;
107
- readonly op: "=" | "<>" | "<" | "<=" | ">" | ">=";
108
- readonly value: {
109
- readonly type: "boolean";
110
- readonly value: boolean;
111
- } | {
112
- readonly type: "integer";
113
- readonly value: string;
114
- } | {
115
- readonly type: "string";
116
- readonly value: string;
117
- };
118
- } | undefined;
119
- readonly isReplicaIdentity?: boolean | undefined;
120
- readonly isPrimaryKey?: boolean | undefined;
121
- readonly isImmediate?: boolean | undefined;
122
- readonly name: string;
123
- readonly columns: {
124
- readonly [x: string]: "ASC" | "DESC";
125
- };
126
- readonly schema: string;
127
- readonly tableName: string;
128
- readonly unique: boolean;
6
+ isReplicaIdentity?: boolean | undefined;
7
+ isPrimaryKey?: boolean | undefined;
8
+ isImmediate?: boolean | undefined;
9
+ name: string;
10
+ columns: Record<string, "ASC" | "DESC">;
11
+ schema: string;
12
+ tableName: string;
13
+ unique: boolean;
129
14
  }[];
130
15
  tables: {
131
16
  replicaIdentityColumns: string[];
132
- columns: {
133
- [k: string]: {
134
- pgTypeClass?: "e" | "d" | "b" | "c" | "p" | "r" | "m" | undefined;
135
- elemPgTypeClass?: "e" | "d" | "b" | "c" | "p" | "r" | "m" | null | undefined;
136
- characterMaximumLength?: number | null | undefined;
137
- notNull?: boolean | null | undefined;
138
- dflt?: string | null | undefined;
139
- pos: number;
140
- dataType: string;
141
- typeOID: number;
142
- };
143
- };
144
17
  primaryKey?: string[] | undefined;
145
18
  schemaOID?: number | undefined;
146
19
  replicaIdentity?: "n" | "d" | "f" | "i" | undefined;
147
20
  name: string;
148
21
  schema: string;
22
+ oid: number;
23
+ columns: Record<string, {
24
+ pgTypeClass?: "e" | "d" | "b" | "c" | "p" | "r" | "m" | undefined;
25
+ elemPgTypeClass?: "e" | "d" | "b" | "c" | "p" | "r" | "m" | null | undefined;
26
+ characterMaximumLength?: number | null | undefined;
27
+ notNull?: boolean | null | undefined;
28
+ dflt?: string | null | undefined;
29
+ pos: number;
30
+ dataType: string;
31
+ typeOID: number;
32
+ }>;
149
33
  publications: Record<string, {
150
34
  rowFilter: string | null;
151
35
  }>;
152
- oid: number;
153
36
  }[];
154
37
  }>;
155
38
  export type PublishedSchema = v.Infer<typeof publishedSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"published.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/published.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAIrC,OAAO,KAAK,CAAC,MAAM,wCAAwC,CAAC;AAc5D,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,sBAAsB,CAAC;AAE9B,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,qBAAqB,UAAO,UA+K7B;AAcD,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;CAC7C,CAAC;AAMF,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,eAAe,GACtB,SAAS,sBAAsB,EAAE,CAEnC;AAED,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,eAAe,EACvB,MAAM,GAAE,GAAG,CAAC,MAAM,CAAa,GAC9B,IAAI,CAWN;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4FxB,CAAC;AAEL,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,MAAM,iCAAiC,GAC3C,eAAe,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAUpC,QAAA,MAAM,wBAAwB;;;;;;cAA6B,CAAC;AAE5D,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACxD,CAAC;AAEF;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,QAAQ,CAAC,GAAG,EACjB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,eAAe,CAAC,CAsD1B"}
1
+ {"version":3,"file":"published.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/published.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAIrC,OAAO,KAAK,CAAC,MAAM,wCAAwC,CAAC;AAS5D,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,UAmKnE;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDxB,CAAC;AAEL,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,MAAM,iCAAiC,GAC3C,eAAe,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAUpC,QAAA,MAAM,wBAAwB;;;;;;cAA6B,CAAC;AAE5D,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACxD,CAAC;AAEF;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,QAAQ,CAAC,GAAG,EACjB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,eAAe,CAAC,CAmD1B"}
@@ -2,15 +2,13 @@ import { assert } from "../../../../../../shared/src/asserts.js";
2
2
  import { parse, valita_exports } from "../../../../../../shared/src/valita.js";
3
3
  import { equals } from "../../../../../../shared/src/set-utils.js";
4
4
  import { BigIntJSON } from "../../../../../../shared/src/bigint-json.js";
5
- import { publishedColumnSpec, publishedIndexSpec, publishedTableSpec } from "../../../../db/specs.js";
5
+ import { publishedIndexSpec, publishedTableSpec } from "../../../../db/specs.js";
6
6
  import { liteTableName } from "../../../../types/names.js";
7
7
  import { mapPostgresToLite, mapPostgresToLiteIndex } from "../../../../db/pg-to-lite.js";
8
8
  import { computeZqlSpecsFromLiteSpecs } from "../../../../db/lite-tables.js";
9
- import { getOrCreateCounter } from "../../../../observability/metrics.js";
10
- import { translatePostgresIndexPredicate } from "./index-predicate.js";
11
9
  import { literal } from "pg-format";
12
10
  //#region ../zero-cache/src/services/change-source/pg/schema/published.ts
13
- function publishedSchemaQuery(publications, includePartialIndexes = true) {
11
+ function publishedSchemaQuery(publications) {
14
12
  return `
15
13
  WITH published_columns AS (SELECT
16
14
  pc.oid::int8 AS "oid",
@@ -24,7 +22,6 @@ WITH published_columns AS (SELECT
24
22
  atttypid::int8 AS "typeOID",
25
23
  pt.typtype,
26
24
  elem_pt.typtype AS "elemTyptype",
27
- coll.collisdeterministic AS "collationIsDeterministic",
28
25
  NULLIF(atttypmod, -1) AS "maxLen",
29
26
  attndims "arrayDims",
30
27
  attnotnull AS "notNull",
@@ -37,7 +34,6 @@ JOIN pg_class pc ON pc.oid = attrelid
37
34
  JOIN pg_namespace pns ON pns.oid = relnamespace
38
35
  JOIN pg_type pt ON atttypid = pt.oid
39
36
  LEFT JOIN pg_type elem_pt ON elem_pt.oid = pt.typelem
40
- LEFT JOIN pg_collation coll ON coll.oid = pg_attribute.attcollation
41
37
  JOIN pg_publication_tables as pb ON
42
38
  pb.schemaname = nspname AND
43
39
  pb.tablename = pc.relname AND
@@ -65,7 +61,6 @@ tables AS (SELECT json_build_object(
65
61
  'pgTypeClass', "typtype",
66
62
  'elemPgTypeClass', "elemTyptype",
67
63
  'typeOID', "typeOID",
68
- 'collationIsDeterministic', "collationIsDeterministic",
69
64
  -- https://stackoverflow.com/a/52376230
70
65
  'characterMaximumLength', CASE WHEN "typeOID" = 1043 OR "typeOID" = 1042
71
66
  THEN "maxLen" - 4
@@ -95,17 +90,10 @@ tables AS (SELECT json_build_object(
95
90
  pg_indexes.indexname as "name",
96
91
  index_column.name as "col",
97
92
  CASE WHEN pg_index.indoption[index_column.pos-1] & 1 = 1 THEN 'DESC' ELSE 'ASC' END as "dir",
98
- -- Partial indexes are always reported as non-unique. Nothing on the
99
- -- replica relies on the uniqueness of a partial index (it cannot serve
100
- -- as a row key), and reporting it as non-unique keeps the ddl event
101
- -- format compatible with older versions of the zero-cache, which
102
- -- ignore "predicateSQL" and would otherwise create a full UNIQUE index
103
- -- on the replica.
104
- (pg_index.indisunique AND pg_index.indpred IS NULL) as "unique",
93
+ pg_index.indisunique as "unique",
105
94
  pg_index.indisprimary as "isPrimaryKey",
106
95
  pg_index.indisreplident as "isReplicaIdentity",
107
- pg_index.indimmediate as "isImmediate",
108
- pg_get_expr(pg_index.indpred, pg_index.indrelid, false) as "predicateSQL"
96
+ pg_index.indimmediate as "isImmediate"
109
97
  FROM pg_indexes
110
98
  JOIN pg_namespace ON pg_indexes.schemaname = pg_namespace.nspname
111
99
  JOIN pg_class pc ON
@@ -129,7 +117,7 @@ tables AS (SELECT json_build_object(
129
117
  LEFT JOIN pg_constraint ON pg_constraint.conindid = pc.oid
130
118
  WHERE pb.pubname IN (${literal(publications)})
131
119
  AND pg_index.indexprs IS NULL
132
- AND (${includePartialIndexes} OR pg_index.indpred IS NULL)
120
+ AND pg_index.indpred IS NULL
133
121
  AND (pg_constraint.contype IS NULL OR pg_constraint.contype IN ('p', 'u'))
134
122
  AND indexed.attnames <@ pb.attnames
135
123
  AND (current_setting('server_version_num')::int >= 160000 OR false = ALL(indexed.generated))
@@ -147,11 +135,10 @@ tables AS (SELECT json_build_object(
147
135
  'isPrimaryKey', "isPrimaryKey",
148
136
  'isReplicaIdentity', "isReplicaIdentity",
149
137
  'isImmediate', "isImmediate",
150
- 'predicateSQL', "predicateSQL",
151
138
  'columns', json_object_agg("col", "dir")
152
139
  ) AS index FROM indexed_columns
153
140
  GROUP BY "schema", "tableName", "name", "unique",
154
- "isPrimaryKey", "isReplicaIdentity", "isImmediate", "predicateSQL")
141
+ "isPrimaryKey", "isReplicaIdentity", "isImmediate")
155
142
 
156
143
  SELECT json_build_object(
157
144
  'tables', COALESCE((SELECT json_agg("table") FROM tables), '[]'::json),
@@ -159,63 +146,12 @@ tables AS (SELECT json_build_object(
159
146
  ) as "publishedSchema"
160
147
  `;
161
148
  }
162
- var rawPublishedColumnSpec = publishedColumnSpec.extend({ collationIsDeterministic: valita_exports.boolean().nullable().optional() });
163
- var rawPublishedTableSpec = publishedTableSpec.extend({ columns: valita_exports.record(rawPublishedColumnSpec) });
164
- var rawPublishedIndexSpec = publishedIndexSpec.extend({ predicateSQL: valita_exports.string().nullable().optional() });
165
- var skippedIndexDiagnostics = /* @__PURE__ */ new WeakMap();
166
- function getSkippedIndexDiagnostics(schema) {
167
- return skippedIndexDiagnostics.get(schema) ?? [];
168
- }
169
- function warnForSkippedIndexes(lc, schema, warned = /* @__PURE__ */ new Set()) {
170
- for (const diagnostic of getSkippedIndexDiagnostics(schema)) {
171
- const key = JSON.stringify(diagnostic);
172
- if (warned.has(key)) continue;
173
- warned.add(key);
174
- lc.warn?.("Skipping unsupported PostgreSQL partial index", diagnostic);
175
- getOrCreateCounter("replication", "skipped_index", {
176
- description: "Number of PostgreSQL indexes skipped during replication.",
177
- unit: "{index}"
178
- }).add(1, {
179
- reason: diagnostic.reason,
180
- partial: true
181
- });
182
- }
183
- }
184
149
  var publishedSchema = valita_exports.object({
185
- tables: valita_exports.array(rawPublishedTableSpec),
186
- indexes: valita_exports.array(rawPublishedIndexSpec)
187
- }).map(({ tables: rawTables, indexes: rawIndexes }) => {
188
- const diagnostics = [];
189
- const indexes = [];
190
- for (const rawIndex of rawIndexes) {
191
- const { predicateSQL, ...index } = rawIndex;
192
- if (predicateSQL !== null && predicateSQL !== void 0) {
193
- const table = rawTables.find((candidate) => candidate.schema === index.schema && candidate.name === index.tableName);
194
- const translation = table ? translatePostgresIndexPredicate(predicateSQL, table) : {
195
- supported: false,
196
- reason: "unpublished-column"
197
- };
198
- if (!translation.supported) {
199
- diagnostics.push({
200
- schema: index.schema,
201
- index: index.name,
202
- predicate: predicateSQL,
203
- reason: translation.reason
204
- });
205
- continue;
206
- }
207
- indexes.push({
208
- ...index,
209
- predicate: translation.predicate
210
- });
211
- } else indexes.push(index);
212
- }
213
- const tables = rawTables.map((table) => ({
214
- ...table,
215
- columns: Object.fromEntries(Object.entries(table.columns).map(([name, { collationIsDeterministic: _, ...column }]) => [name, column]))
216
- }));
150
+ tables: valita_exports.array(publishedTableSpec),
151
+ indexes: valita_exports.array(publishedIndexSpec)
152
+ }).map(({ tables, indexes }) => {
217
153
  const zqlSpecs = computeZqlSpecsFromLiteSpecs(tables.map((t) => mapPostgresToLite(t)), indexes.map(mapPostgresToLiteIndex), { includeBackfillingColumns: true });
218
- const result = {
154
+ return {
219
155
  indexes,
220
156
  tables: tables.map((table) => {
221
157
  const replicaIdentityColumns = [];
@@ -238,8 +174,6 @@ var publishedSchema = valita_exports.object({
238
174
  };
239
175
  })
240
176
  };
241
- skippedIndexDiagnostics.set(result, diagnostics);
242
- return result;
243
177
  });
244
178
  var publicationSchema = valita_exports.object({
245
179
  pubname: valita_exports.string(),
@@ -278,15 +212,12 @@ async function getPublicationInfo(sql, publications) {
278
212
  });
279
213
  }
280
214
  assert(result[2][0].publishedSchema, () => `Invalid publishedSchema result ${BigIntJSON.stringify(result[2])}`);
281
- const schema = parse(result[2][0].publishedSchema, publishedSchema);
282
- const info = {
215
+ return {
283
216
  publications: parse(result[1], publicationsResultSchema),
284
- ...schema
217
+ ...parse(result[2][0].publishedSchema, publishedSchema)
285
218
  };
286
- skippedIndexDiagnostics.set(info, getSkippedIndexDiagnostics(schema));
287
- return info;
288
219
  }
289
220
  //#endregion
290
- export { getPublicationInfo, publishedSchema, publishedSchemaQuery, warnForSkippedIndexes };
221
+ export { getPublicationInfo, publishedSchema, publishedSchemaQuery };
291
222
 
292
223
  //# sourceMappingURL=published.js.map