11/**
22 * Row-write integration tests against the provisioned, disposable TEST_DATABASE_URL database (the
3- * integration setup points DATABASE_URL at it too). The migrated `rows_version` triggers, the
4- * row-order advisory lock, and the filtered bulk-mutation queries all run for real.
3+ * integration setup points DATABASE_URL at it too). The row-order advisory lock and the filtered
4+ * bulk-mutation queries run for real everywhere; the `rows_version` cases also need the migrated
5+ * triggers and skip on a `db:push` schema.
56 */
67import { db } from '@sim/db'
78import { userTableDefinitions , userTableRows } from '@sim/db/schema'
@@ -44,11 +45,15 @@ const control = postgres(url, { max: 4, onnotice: () => {} })
4445const workspaceId = generateId ( )
4546const userId = generateId ( )
4647
47- /** `db:push` never installs the migration-only triggers these assertions depend on. */
48- const [ { migrated } ] = await control < { migrated : boolean } [ ] > `SELECT EXISTS (
49- SELECT 1 FROM pg_trigger WHERE tgname = 'user_table_rows_version_update_trigger'
50- ) AS migrated`
51- if ( ! migrated ) await control . end ( )
48+ /**
49+ * `db:push` never installs migration-only objects: the `rows_version` triggers and the
50+ * `(workspace_id, table_id)` dependency statistic. Only the cases that depend on them need them.
51+ */
52+ const [ { migrated } ] = await control < { migrated : boolean } [ ] > `SELECT
53+ EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'user_table_rows_version_update_trigger')
54+ AND EXISTS (
55+ SELECT 1 FROM pg_statistic_ext WHERE stxname = 'user_table_rows_workspace_table_stats'
56+ ) AS migrated`
5257
5358async function createTable ( columns : ColumnDefinition [ ] ) : Promise < TableDefinition > {
5459 const id = generateId ( )
@@ -85,7 +90,7 @@ function planNodes(plan: QueryPlan): QueryPlan[] {
8590const textColumns = ( ...ids : string [ ] ) : ColumnDefinition [ ] =>
8691 ids . map ( ( id ) => ( { id, name : id , type : 'string' } ) )
8792
88- describe . skipIf ( ! migrated ) ( 'table row writes against real PostgreSQL' , ( ) => {
93+ describe ( 'table row writes against real PostgreSQL' , ( ) => {
8994 beforeAll ( async ( ) => {
9095 await control `INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
9196 VALUES (${ userId } , 'Row write fixture', ${ `${ userId } @example.test` } , true, now(), now())`
@@ -103,7 +108,7 @@ describe.skipIf(!migrated)('table row writes against real PostgreSQL', () => {
103108 await control . end ( )
104109 } )
105110
106- describe ( 'rows_version' , ( ) => {
111+ describe . skipIf ( ! migrated ) ( 'rows_version' , ( ) => {
107112 it ( 'advances once for a transaction that edits cells across several statements' , async ( ) => {
108113 const table = await createTable ( textColumns ( 'name' ) )
109114 await seedRows ( table . id , [
@@ -379,7 +384,8 @@ describe.skipIf(!migrated)('table row writes against real PostgreSQL', () => {
379384 expect ( [ ...result . affectedRowIds ] . sort ( ) ) . toEqual ( [ ...expected ] . sort ( ) )
380385 } )
381386
382- it ( 'fences only a containment filter into a GIN probe and leaves any other filter unfenced' , async ( ) => {
387+ /** A table large enough that the planner's index choice reflects its statistics. */
388+ async function seedPlanFixture ( ) : Promise < ( filter : Filter ) => Promise < QueryPlan [ ] > > {
383389 const table = await createTable ( textColumns ( 'kind' , 'n' ) )
384390 await control `INSERT INTO user_table_rows (id, table_id, workspace_id, data, order_key)
385391 SELECT ${ table . id } || '-' || n, ${ table . id } , ${ workspaceId } ,
@@ -388,7 +394,7 @@ describe.skipIf(!migrated)('table row writes against real PostgreSQL', () => {
388394 FROM generate_series(1, 20000) AS n`
389395 await control `ANALYZE user_table_rows`
390396
391- async function planFor ( filter : Filter ) : Promise < QueryPlan [ ] > {
397+ return async ( filter ) = > {
392398 const filterClause = buildFilterClause (
393399 filter ,
394400 USER_TABLE_ROWS_SQL_NAME ,
@@ -397,25 +403,35 @@ describe.skipIf(!migrated)('table row writes against real PostgreSQL', () => {
397403 if ( ! filterClause ) throw new Error ( 'Fixture filter compiled to no clause' )
398404 const plans = await withSeqscanOff ( ( trx ) =>
399405 trx . execute < { 'QUERY PLAN' : { Plan : QueryPlan } [ ] } > (
400- sql `EXPLAIN (FORMAT JSON) ${ firstMatchingRowsQuery ( table , filter , filterClause , 5 ) } `
406+ sql `EXPLAIN (FORMAT JSON) ${ firstMatchingRowsQuery ( table , filter , filterClause , 5 , 'rows' ) } `
401407 )
402408 )
403409 return planNodes ( plans [ 0 ] [ 'QUERY PLAN' ] [ 0 ] . Plan )
404410 }
411+ }
412+
413+ it ( 'fences only a containment filter and leaves any other filter unfenced' , async ( ) => {
414+ const planFor = await seedPlanFixture ( )
405415
406416 const fenced = await planFor ( { kind : 'hit' } )
407417 expect ( fenced . some ( ( node ) => node [ 'Node Type' ] === 'CTE Scan' ) ) . toBe ( true )
418+
419+ const walked = await planFor ( { kind : { $ne : 'miss' } } )
420+ expect ( walked . some ( ( node ) => node [ 'Node Type' ] === 'CTE Scan' ) ) . toBe ( false )
421+ expect ( walked [ 0 ] [ 'Node Type' ] ) . toBe ( 'Limit' )
422+ } )
423+
424+ it . skipIf ( ! migrated ) ( 'probes the tenant GIN index inside the fence' , async ( ) => {
425+ const planFor = await seedPlanFixture ( )
426+
427+ const fenced = await planFor ( { kind : 'hit' } )
408428 expect (
409429 fenced . some (
410430 ( node ) =>
411431 node [ 'Node Type' ] === 'Bitmap Index Scan' &&
412432 node [ 'Index Name' ] === 'user_table_rows_tenant_data_gin_idx'
413433 )
414434 ) . toBe ( true )
415-
416- const walked = await planFor ( { kind : { $ne : 'miss' } } )
417- expect ( walked . some ( ( node ) => node [ 'Node Type' ] === 'CTE Scan' ) ) . toBe ( false )
418- expect ( walked [ 0 ] [ 'Node Type' ] ) . toBe ( 'Limit' )
419435 } )
420436 } )
421437
0 commit comments