@@ -2363,6 +2363,69 @@ reservedWordColumnTests connSource =
23632363
23642364 freshTestDB step
23652365
2366+ step " Definition using the legacy quoted form (\"\\\" timestamp\\\"\" ) still validates"
2367+ assertNoException " Quoted-keyword column in tblIndexes is accepted" $ do
2368+ let definitions = tableDefsWithPgCrypto [tableWithLegacyQuotedIndex]
2369+ migrateDatabase
2370+ defaultExtrasOptions
2371+ definitions
2372+ [createTableMigration tableWithLegacyQuotedIndex]
2373+ checkDatabase defaultExtrasOptions definitions
2374+
2375+ freshTestDB step
2376+
2377+ step " Index pre-existing under the quoted-source name validates"
2378+ assertNoException " Quoted-source index name is accepted by checkDatabase" $ do
2379+ -- Hand-roll the index under its canonical @$ident$@ name, then validate
2380+ -- against a definition that includes it.
2381+ migrateDatabase
2382+ defaultExtrasOptions
2383+ (tableDefsWithPgCrypto [tableWithLegacyQuotedIndexNoIndexes])
2384+ [createTableMigration tableWithLegacyQuotedIndexNoIndexes]
2385+ runSQL_
2386+ " CREATE INDEX \" idx__reserved_word_test5__$timestamp$\" \
2387+ \ON reserved_word_test5 (\" timestamp\" )"
2388+ checkDatabase defaultExtrasOptions $
2389+ tableDefsWithPgCrypto [tableWithLegacyQuotedIndex]
2390+
2391+ freshTestDB step
2392+
2393+ step " Index with a reserved-word column in INCLUDE validates"
2394+ assertNoException " Reserved-word column in idxInclude is accepted" $ do
2395+ let definitions = tableDefsWithPgCrypto [tableWithReservedWordInclude]
2396+ migrateDatabase
2397+ defaultExtrasOptions
2398+ definitions
2399+ [createTableMigration tableWithReservedWordInclude]
2400+ checkDatabase defaultExtrasOptions definitions
2401+
2402+ freshTestDB step
2403+
2404+ step " Multi-column index mixing unquoted and quoted-keyword columns validates"
2405+ assertNoException " Mixed quoted/unquoted composite index is accepted" $ do
2406+ let definitions = tableDefsWithPgCrypto [tableWithMixedQuotedComposite]
2407+ migrateDatabase
2408+ defaultExtrasOptions
2409+ definitions
2410+ [createTableMigration tableWithMixedQuotedComposite]
2411+ checkDatabase defaultExtrasOptions definitions
2412+
2413+ freshTestDB step
2414+
2415+ step " Index on a JSONB expression is created without being quoted as an identifier"
2416+ assertNoException " Expression index is accepted" $ do
2417+ let definitions = tableDefsWithPgCrypto [tableWithExpressionIndex]
2418+ migrateDatabase
2419+ defaultExtrasOptions
2420+ definitions
2421+ [createTableMigration tableWithExpressionIndex]
2422+ checkDatabase defaultExtrasOptions definitions
2423+
2424+ freshTestDB step
2425+
2426+ -- NB: keep this step last. The CREATE INDEX failure on a reserved-word
2427+ -- column leaves the connection's transaction in aborted state, so a
2428+ -- subsequent `freshTestDB` (which runs DROP SCHEMA) fails.
23662429 step " Attempt to create a table with an index on a 'select' column"
23672430 assertException " Table with index on 'select' column can't be created" $ do
23682431 let definitions = tableDefsWithPgCrypto [tableWithSelectIndex]
@@ -2421,6 +2484,84 @@ reservedWordColumnTests connSource =
24212484 , tblIndexes = [indexOnColumn " select" ]
24222485 }
24232486
2487+ tableWithLegacyQuotedIndex =
2488+ tblTable
2489+ { tblName = " reserved_word_test5"
2490+ , tblVersion = 1
2491+ , tblColumns =
2492+ [ tblColumn {colName = " id" , colType = UuidT , colNullable = False , colDefault = Just " gen_random_uuid()" }
2493+ , tblColumn {colName = " timestamp" , colType = TimestampWithZoneT , colNullable = False }
2494+ ]
2495+ , tblPrimaryKey = pkOnColumn " id"
2496+ , tblIndexes = [indexOnColumn " \" timestamp\" " ]
2497+ }
2498+
2499+ tableWithLegacyQuotedIndexNoIndexes = tableWithLegacyQuotedIndex {tblIndexes = [] }
2500+
2501+ tableWithReservedWordInclude =
2502+ tblTable
2503+ { tblName = " reserved_word_test6"
2504+ , tblVersion = 1
2505+ , tblColumns =
2506+ [ tblColumn {colName = " id" , colType = UuidT , colNullable = False , colDefault = Just " gen_random_uuid()" }
2507+ , tblColumn {colName = " name" , colType = TextT , colNullable = False }
2508+ , tblColumn {colName = " timestamp" , colType = TimestampWithZoneT , colNullable = False }
2509+ ]
2510+ , tblPrimaryKey = pkOnColumn " id"
2511+ , tblIndexes = [(uniqueIndexOnColumn " name" ) {idxInclude = [" timestamp" ]}]
2512+ }
2513+
2514+ tableWithMixedQuotedComposite =
2515+ tblTable
2516+ { tblName = " reserved_word_test7"
2517+ , tblVersion = 1
2518+ , tblColumns =
2519+ [ tblColumn {colName = " id" , colType = UuidT , colNullable = False , colDefault = Just " gen_random_uuid()" }
2520+ , tblColumn {colName = " other_id" , colType = UuidT , colNullable = False }
2521+ , tblColumn {colName = " name" , colType = TextT , colNullable = False }
2522+ , tblColumn {colName = " time" , colType = TimestampWithZoneT , colNullable = False }
2523+ ]
2524+ , tblPrimaryKey = pkOnColumn " id"
2525+ , tblIndexes = [indexOnColumns [" other_id" , " name" , " \" time\" " ]]
2526+ }
2527+
2528+ tableWithExpressionIndex =
2529+ tblTable
2530+ { tblName = " reserved_word_test8"
2531+ , tblVersion = 1
2532+ , tblColumns =
2533+ [ tblColumn {colName = " id" , colType = UuidT , colNullable = False , colDefault = Just " gen_random_uuid()" }
2534+ , tblColumn {colName = " data" , colType = JsonbT , colNullable = False }
2535+ ]
2536+ , tblPrimaryKey = pkOnColumn " id"
2537+ , tblIndexes = [uniqueIndexOnColumn (indexColumn " (data ->> 'key'::text)" )]
2538+ }
2539+
2540+ sqlCreateIndexEmissionTests :: TestTree
2541+ sqlCreateIndexEmissionTests =
2542+ testGroup
2543+ " sqlCreateIndex emission"
2544+ [ testCase " expression column is emitted without identifier quoting" $
2545+ assertCreateIndex
2546+ " CREATE UNIQUE INDEX unique_idx__t__$data$key$text$\
2547+ \ ON t USING BTree ((data ->> 'key'::text))"
2548+ (uniqueIndexOnColumn (indexColumn " (data ->> 'key'::text)" ))
2549+ , testCase " quoted reserved-word column round-trips as a quoted identifier" $
2550+ assertCreateIndex
2551+ " CREATE INDEX idx__t__$timestamp$ ON t USING BTree (\" timestamp\" )"
2552+ (indexOnColumn " \" timestamp\" " )
2553+ , testCase " unquoted column is emitted bare" $
2554+ assertCreateIndex
2555+ " CREATE INDEX idx__t__name ON t USING BTree (name)"
2556+ (indexOnColumn " name" )
2557+ ]
2558+ where
2559+ assertCreateIndex expected idx =
2560+ assertEqual
2561+ " sqlCreateIndexMaybeDowntime output"
2562+ expected
2563+ (unRawSQL (sqlCreateIndexMaybeDowntime " t" idx))
2564+
24242565sqlAnyAllTests :: TestTree
24252566sqlAnyAllTests =
24262567 testGroup
@@ -2721,6 +2862,7 @@ main = do
27212862 , overlapingIndexesTests connSource
27222863 , nullsNotDistinctTests connSource
27232864 , reservedWordColumnTests connSource
2865+ , sqlCreateIndexEmissionTests
27242866 , sqlAnyAllTests
27252867 , enumTest connSource
27262868 , numericColumnTypeTest connSource
0 commit comments