@@ -30,12 +30,10 @@ export function declareConcatTable<
3030 throw new StackAssertionError ( "declareConcatTable requires at least one input table" , { tableId : options . tableId } ) ;
3131 } ) ( ) ;
3232 const referenceCompareGroupKeysSql = firstTable . compareGroupKeys ( sqlExpression `$1` , sqlExpression `$2` ) . sql ;
33- const referenceCompareSortKeysSql = firstTable . compareSortKeys ( sqlExpression `$1` , sqlExpression `$2` ) . sql ;
3433 for ( const table of tables ) {
3534 const compareGroupKeysSql = table . compareGroupKeys ( sqlExpression `$1` , sqlExpression `$2` ) . sql ;
36- const compareSortKeysSql = table . compareSortKeys ( sqlExpression `$1` , sqlExpression `$2` ) . sql ;
37- if ( compareGroupKeysSql !== referenceCompareGroupKeysSql || compareSortKeysSql !== referenceCompareSortKeysSql ) {
38- throw new StackAssertionError ( "declareConcatTable requires comparator-compatible input tables" , {
35+ if ( compareGroupKeysSql !== referenceCompareGroupKeysSql ) {
36+ throw new StackAssertionError ( "declareConcatTable requires group-comparator-compatible input tables" , {
3937 tableId : options . tableId ,
4038 tableDebugId : tableIdToDebugString ( table . tableId ) ,
4139 } ) ;
@@ -98,27 +96,43 @@ export function declareConcatTable<
9896 ` ;
9997 } ) . join ( "\nUNION ALL\n" ) ;
10098 } ;
101-
102- tables . forEach ( ( table , tableIndex ) => {
103- table . registerRowChangeTrigger ( ( changesTable ) => {
104- const concatChangesTableName = `concat_changes_${ generateSecureRandomString ( ) } ` ;
105- return [
106- sqlQuery `
107- SELECT
108- "changes"."groupKey" AS "groupKey",
109- ${ rawExpression < RowIdentifier > ( createConcatenatedRowIdentifierSql ( tableIndex , `"changes"."rowIdentifier"` ) ) } AS "rowIdentifier",
110- 'null'::jsonb AS "oldRowSortKey",
111- 'null'::jsonb AS "newRowSortKey",
112- "changes"."oldRowData" AS "oldRowData",
113- "changes"."newRowData" AS "newRowData"
114- FROM ${ changesTable } AS "changes"
115- WHERE ${ isInitializedExpression }
116- AND ${ rawExpression < boolean > ( getInputInitializedSql ( table ) ) }
117- ` . toStatement ( concatChangesTableName ) ,
118- ...[ ...triggers . values ( ) ] . flatMap ( ( trigger ) => trigger ( quoteSqlIdentifier ( concatChangesTableName ) ) ) ,
119- ] ;
99+ const createInputTriggerStatements = (
100+ table : Table < GK , any , RD > ,
101+ tableIndex : number ,
102+ changesTable : SqlExpression < { __brand : "$SQL_Table" } > ,
103+ ) => {
104+ const concatChangesTableName = `concat_changes_${ generateSecureRandomString ( ) } ` ;
105+ return [
106+ sqlQuery `
107+ SELECT
108+ "changes"."groupKey" AS "groupKey",
109+ ${ rawExpression < RowIdentifier > ( createConcatenatedRowIdentifierSql ( tableIndex , `"changes"."rowIdentifier"` ) ) } AS "rowIdentifier",
110+ 'null'::jsonb AS "oldRowSortKey",
111+ 'null'::jsonb AS "newRowSortKey",
112+ "changes"."oldRowData" AS "oldRowData",
113+ "changes"."newRowData" AS "newRowData"
114+ FROM ${ changesTable } AS "changes"
115+ WHERE ${ isInitializedExpression }
116+ AND ${ rawExpression < boolean > ( getInputInitializedSql ( table ) ) }
117+ ` . toStatement ( concatChangesTableName ) ,
118+ ...[ ...triggers . values ( ) ] . flatMap ( ( trigger ) => trigger ( quoteSqlIdentifier ( concatChangesTableName ) ) ) ,
119+ ] ;
120+ } ;
121+ let inputTriggerRegistrations : Array < { deregister : ( ) => void } > = [ ] ;
122+ const ensureInputTriggerRegistrations = ( ) => {
123+ if ( inputTriggerRegistrations . length > 0 ) return ;
124+ inputTriggerRegistrations = tables . map ( ( table , tableIndex ) => {
125+ return table . registerRowChangeTrigger ( ( changesTable ) => {
126+ return createInputTriggerStatements ( table , tableIndex , changesTable ) ;
127+ } ) ;
120128 } ) ;
121- } ) ;
129+ } ;
130+ const deregisterInputTriggers = ( ) => {
131+ for ( const registration of inputTriggerRegistrations ) {
132+ registration . deregister ( ) ;
133+ }
134+ inputTriggerRegistrations = [ ] ;
135+ } ;
122136
123137 return {
124138 tableId : options . tableId ,
@@ -166,25 +180,31 @@ export function declareConcatTable<
166180 ` ,
167181 compareGroupKeys : firstTable . compareGroupKeys ,
168182 compareSortKeys : ( ) => sqlExpression `0` ,
169- init : ( ) => [ sqlStatement `
170- INSERT INTO "BulldozerStorageEngine" ("id", "keyPath", "value")
171- VALUES
172- (gen_random_uuid(), ${ getTablePath ( options . tableId ) } , 'null'::jsonb),
173- (gen_random_uuid(), ${ sqlArray ( [ ...getTablePathSegments ( options . tableId ) , quoteSqlJsonbLiteral ( "table" ) ] ) } ::jsonb[], 'null'::jsonb),
174- (gen_random_uuid(), ${ getStorageEnginePath ( options . tableId , [ ] ) } ::jsonb[], 'null'::jsonb),
175- (gen_random_uuid(), ${ getStorageEnginePath ( options . tableId , [ "metadata" ] ) } ::jsonb[], '{ "version": 1 }'::jsonb)
176- ` ] ,
177- delete : ( ) => [ sqlStatement `
178- WITH RECURSIVE "pathsToDelete" AS (
179- SELECT ${ getTablePath ( options . tableId ) } ::jsonb[] AS "path"
180- UNION ALL
181- SELECT "BulldozerStorageEngine"."keyPath" AS "path"
182- FROM "BulldozerStorageEngine"
183- INNER JOIN "pathsToDelete" ON "BulldozerStorageEngine"."keyPathParent" = "pathsToDelete"."path"
184- )
185- DELETE FROM "BulldozerStorageEngine"
186- WHERE "keyPath" IN (SELECT "path" FROM "pathsToDelete")
187- ` ] ,
183+ init : ( ) => {
184+ ensureInputTriggerRegistrations ( ) ;
185+ return [ sqlStatement `
186+ INSERT INTO "BulldozerStorageEngine" ("id", "keyPath", "value")
187+ VALUES
188+ (gen_random_uuid(), ${ getTablePath ( options . tableId ) } , 'null'::jsonb),
189+ (gen_random_uuid(), ${ sqlArray ( [ ...getTablePathSegments ( options . tableId ) , quoteSqlJsonbLiteral ( "table" ) ] ) } ::jsonb[], 'null'::jsonb),
190+ (gen_random_uuid(), ${ getStorageEnginePath ( options . tableId , [ ] ) } ::jsonb[], 'null'::jsonb),
191+ (gen_random_uuid(), ${ getStorageEnginePath ( options . tableId , [ "metadata" ] ) } ::jsonb[], '{ "version": 1 }'::jsonb)
192+ ` ] ;
193+ } ,
194+ delete : ( ) => {
195+ deregisterInputTriggers ( ) ;
196+ return [ sqlStatement `
197+ WITH RECURSIVE "pathsToDelete" AS (
198+ SELECT ${ getTablePath ( options . tableId ) } ::jsonb[] AS "path"
199+ UNION ALL
200+ SELECT "BulldozerStorageEngine"."keyPath" AS "path"
201+ FROM "BulldozerStorageEngine"
202+ INNER JOIN "pathsToDelete" ON "BulldozerStorageEngine"."keyPathParent" = "pathsToDelete"."path"
203+ )
204+ DELETE FROM "BulldozerStorageEngine"
205+ WHERE "keyPath" IN (SELECT "path" FROM "pathsToDelete")
206+ ` ] ;
207+ } ,
188208 isInitialized : ( ) => isInitializedExpression ,
189209 registerRowChangeTrigger : ( trigger ) => {
190210 const id = generateSecureRandomString ( ) ;
0 commit comments