diff --git a/expected-schema/v16/schemas/public/tables/some_new_table/objrep b/expected-schema/v16/schemas/public/tables/some_new_table/objrep new file mode 100644 index 00000000..4812abfb --- /dev/null +++ b/expected-schema/v16/schemas/public/tables/some_new_table/objrep @@ -0,0 +1,23 @@ +{ + "accessmethod": "heap", + "amoptions": null, + "columns_in_order": [], + "compositetype": null, + "forcerowsecurity": false, + "kind": "r", + "owner": "codd_admin", + "partition": false, + "persistence": "p", + "privileges": { + "codd_admin": [ + [ + "daxrtDw", + "codd_admin" + ] + ] + }, + "replident": "d", + "rowsecurity": false, + "rowtype": "some_new_table", + "shared": false +} \ No newline at end of file diff --git a/scripts/wait-for-pg-ready.sh b/scripts/wait-for-pg-ready.sh index d1ff4d4b..0dff05dc 100755 --- a/scripts/wait-for-pg-ready.sh +++ b/scripts/wait-for-pg-ready.sh @@ -2,6 +2,7 @@ set -eo pipefail +PGCTLSTATUS=0 pg_ctl status -D "$PGDATA" || PGCTLSTATUS=$? if [ "$PGCTLSTATUS" = "0" ]; then diff --git a/sql-migrations/all/2021-01-11-22-14-29-some-unique-index.sql b/sql-migrations/all/2021-01-11-22-14-29-some-unique-index.sql index 3d64b385..ab3b4331 100644 --- a/sql-migrations/all/2021-01-11-22-14-29-some-unique-index.sql +++ b/sql-migrations/all/2021-01-11-22-14-29-some-unique-index.sql @@ -1 +1,2 @@ -CREATE UNIQUE INDEX any_unique_employee_idx_will_do ON employee (employee_name); \ No newline at end of file +\restrict pbgv1pF8SxQK6cuT7hwDi21uDYr8wpxKJ3wlLa9Zk5EIO1xBiu84SJQU8fL22PT +CREATE UNIQUE INDEX any_unique_employee_idx_will_do ON employee (employee_name);\unrestrict pbgv1pF8SxQK6cuT7hwDi21uDYr8wpxKJ3wlLa9Zk5EIO1xBiu84SJQU8fL22PT diff --git a/sql-migrations/all/2022-03-08-19-40-06-trivial-select.sql b/sql-migrations/all/2022-03-08-19-40-06-trivial-select.sql index 1f54da89..b72c6673 100644 --- a/sql-migrations/all/2022-03-08-19-40-06-trivial-select.sql +++ b/sql-migrations/all/2022-03-08-19-40-06-trivial-select.sql @@ -1 +1,3 @@ -SELECT 3+25; \ No newline at end of file +\restrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ +SELECT 3+25; +\unrestrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ diff --git a/sql-migrations/all/2025-08-02-12-27-46-animals-view.sql b/sql-migrations/all/2025-08-02-12-27-46-animals-view.sql index 6c81a464..7f025c8d 100644 --- a/sql-migrations/all/2025-08-02-12-27-46-animals-view.sql +++ b/sql-migrations/all/2025-08-02-12-27-46-animals-view.sql @@ -1,4 +1,13 @@ + + +\restrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ + + CREATE VIEW animals_by_name AS SELECT popular_name FROM animals ORDER BY popular_name; + +\unrestrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ + +SELECT 4; diff --git a/sql-migrations/all/2026-03-05-19-39-18-some-dump-with-restrict-unrestrict.sql b/sql-migrations/all/2026-03-05-19-39-18-some-dump-with-restrict-unrestrict.sql new file mode 100644 index 00000000..91a16396 --- /dev/null +++ b/sql-migrations/all/2026-03-05-19-39-18-some-dump-with-restrict-unrestrict.sql @@ -0,0 +1,20 @@ +-- +-- PostgreSQL database dump +-- + +\restrict qNlLbo8p6RcFfsC9fMVyEoGtCxjry6twGZo5QYQG5mvVxeo6RoPOef7VkHrNGZs + +-- Dumped from database version 16.10 +-- Dumped by pg_dump version 16.10 + +-- This is a real pg dump just so we're certain the restrict and unrestrict things are +-- parsed correctly by codd. +-- We add a table here just for the sake of it. +CREATE TABLE some_new_table (); + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict qNlLbo8p6RcFfsC9fMVyEoGtCxjry6twGZo5QYQG5mvVxeo6RoPOef7VkHrNGZs + diff --git a/src/Codd/Internal/MultiQueryStatement.hs b/src/Codd/Internal/MultiQueryStatement.hs index 8e664785..6cab7722 100644 --- a/src/Codd/Internal/MultiQueryStatement.hs +++ b/src/Codd/Internal/MultiQueryStatement.hs @@ -191,6 +191,7 @@ isCountableRunnable = \case WhiteSpacePiece _ -> False BeginTransaction _ -> True CommitTransaction _ -> True + RestrictOrUnrestrictMetaCommand _ -> False RollbackTransaction _ -> True CopyFromStdinStatement _ -> False CopyFromStdinRows _ -> False @@ -220,6 +221,7 @@ runSingleStatementInternal_ :: runSingleStatementInternal_ conn p = case p of CommentPiece _ -> applied WhiteSpacePiece _ -> applied + RestrictOrUnrestrictMetaCommand _ -> applied BeginTransaction s -> singleStatement_ conn s CommitTransaction s -> singleStatement_ conn s RollbackTransaction s -> singleStatement_ conn s diff --git a/src/Codd/Parsing.hs b/src/Codd/Parsing.hs index 49801968..d7a9315e 100644 --- a/src/Codd/Parsing.hs +++ b/src/Codd/Parsing.hs @@ -247,7 +247,7 @@ hoistAddedSqlMigration f (AddedSqlMigration sqlMig tst) = data SectionOption = OptInTxn | OptNoTxn | OptNoParse | OptRequiresCoddSchema deriving stock (Eq, Ord, Show) -data SqlPiece = CommentPiece !Text | WhiteSpacePiece !Text | CopyFromStdinStatement !Text | CopyFromStdinRows !Text | CopyFromStdinEnd !Text | BeginTransaction !Text | RollbackTransaction !Text | CommitTransaction !Text | OtherSqlPiece !Text +data SqlPiece = CommentPiece !Text | WhiteSpacePiece !Text | CopyFromStdinStatement !Text | CopyFromStdinRows !Text | CopyFromStdinEnd !Text | BeginTransaction !Text | RollbackTransaction !Text | CommitTransaction !Text | RestrictOrUnrestrictMetaCommand !Text | OtherSqlPiece !Text deriving stock (Show, Eq) data ParsingException = ParsingException @@ -360,6 +360,7 @@ sqlPieceText (BeginTransaction s) = s sqlPieceText (RollbackTransaction s) = s sqlPieceText (CommitTransaction s) = s sqlPieceText (OtherSqlPiece s) = s +sqlPieceText (RestrictOrUnrestrictMetaCommand s) = s sqlPieceText (CopyFromStdinStatement s) = s sqlPieceText (CopyFromStdinRows s) = s sqlPieceText (CopyFromStdinEnd s) = s @@ -372,6 +373,7 @@ mapSqlPiece f = \case RollbackTransaction s -> RollbackTransaction (f s) CommitTransaction s -> CommitTransaction (f s) OtherSqlPiece s -> OtherSqlPiece (f s) + RestrictOrUnrestrictMetaCommand s -> RestrictOrUnrestrictMetaCommand (f s) CopyFromStdinStatement s -> CopyFromStdinStatement (f s) CopyFromStdinRows s -> CopyFromStdinRows (f s) CopyFromStdinEnd s -> CopyFromStdinEnd (f s) @@ -404,6 +406,8 @@ sqlPieceParser parserState = case parserState of <|> (,OutsideCopy) . CommitTransaction <$> commitTransactionParser + <|> (,OutsideCopy) + <$> restrictOrUnrestrictMetaCommandParser <|> (,OutsideCopy) . OtherSqlPiece <$> anySqlPieceParser @@ -421,6 +425,11 @@ sqlPieceParser parserState = case parserState of commitTransactionParser = spaceSeparatedTokensToParser [CITextToken "COMMIT", AllUntilEndOfStatement] + restrictOrUnrestrictMetaCommandParser = do + r <- string "\\restrict " <|> string "\\unrestrict " + key <- Parsec.takeWhile1 Char.isAlphaNum + eolOrEof <- "" <$ endOfInput <|> eol + pure $ RestrictOrUnrestrictMetaCommand $ r <> key <> eolOrEof anySqlPieceParser = do arbText <- spaceSeparatedTokensToParser [AllUntilEndOfStatement] when (arbText == "") $ fail "Please report this as a bug in codd: trying to parse empty string as SQL piece" diff --git a/test/ParsingSpec.hs b/test/ParsingSpec.hs index 9e424eee..c488b3bd 100644 --- a/test/ParsingSpec.hs +++ b/test/ParsingSpec.hs @@ -260,10 +260,12 @@ genSql onlySyntacticallyValid = do where emptyLineGen = pure "\n" bizarreLineGen = (<> "\n") . Text.pack . getUnicodeString <$> arbitrary + restrictUnrestrictLineGen = elements ["\\restrict pbgv1pF8SxQK6cuT7hwDi21uDYr8wpxKJ3wlLa9Zk5EIO1xBiu84SJQU8fL22PT\n", "\\unrestrict pbgv1pF8SxQK6cuT7hwDi21uDYr8wpxKJ3wlLa9Zk5EIO1xBiu84SJQU8fL22PT\n"] lineGen = frequency [ (if onlySyntacticallyValid then 0 else 1, bizarreLineGen), (1, emptyLineGen), + (1, restrictUnrestrictLineGen), (5, piecesToText <$> genSingleSqlStatement) ] -- Note: the likelihood that QuickCheck will randomly generate text that has a line starting with "-- codd:" @@ -555,7 +557,7 @@ spec = do `shouldBe` groupCopyRows (mconcat origPieces) modifyMaxSuccess (const 10000) $ it - "Statements concatenation matches original and statements end with semi-colon" + "Statements concatenation matches original and some assertions on parsed statements" $ do property $ \SyntacticallyValidRandomSql {..} -> do blks <- @@ -577,6 +579,7 @@ spec = do WhiteSpacePiece t -> t `shouldSatisfy` (\c -> Text.strip c == "") CopyFromStdinEnd ll -> ll `shouldBe` "\\.\n" + RestrictOrUnrestrictMetaCommand t -> t `shouldSatisfy` (\c -> "\\restrict " `Text.isPrefixOf` c || "\\unrestrict " `Text.isPrefixOf` c) _ -> pure () modifyMaxSuccess (const 10000) $ diff --git a/test/migrations/no-txn-partial-application-error-inside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql b/test/migrations/no-txn-partial-application-error-inside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql index 2c7d4e59..bc5e9c85 100644 --- a/test/migrations/no-txn-partial-application-error-inside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql +++ b/test/migrations/no-txn-partial-application-error-inside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql @@ -1,4 +1,5 @@ -- codd: no-txn +\restrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ CREATE TABLE somedata (id INT NOT NULL, UNIQUE(id)); COPY somedata FROM STDIN WITH (FORMAT csv); @@ -6,3 +7,4 @@ COPY somedata FROM STDIN WITH (FORMAT csv); 2 3 \. +\unrestrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ diff --git a/test/migrations/no-txn-partial-application-error-on-commit/2000-01-01-00-00-00-create-table-with-unique-id.sql b/test/migrations/no-txn-partial-application-error-on-commit/2000-01-01-00-00-00-create-table-with-unique-id.sql index 8940e738..98017ac4 100644 --- a/test/migrations/no-txn-partial-application-error-on-commit/2000-01-01-00-00-00-create-table-with-unique-id.sql +++ b/test/migrations/no-txn-partial-application-error-on-commit/2000-01-01-00-00-00-create-table-with-unique-id.sql @@ -1,4 +1,5 @@ -- codd: no-txn +\restrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ CREATE TABLE somedata (id INT NOT NULL, UNIQUE(id) DEFERRABLE INITIALLY DEFERRED); COPY somedata FROM STDIN WITH (FORMAT csv); @@ -6,3 +7,4 @@ COPY somedata FROM STDIN WITH (FORMAT csv); 2 3 \. +\unrestrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ diff --git a/test/migrations/no-txn-partial-application-error-on-commit/2001-01-01-00-00-00-insert-duplicate-inside-explicit-transaction.sql b/test/migrations/no-txn-partial-application-error-on-commit/2001-01-01-00-00-00-insert-duplicate-inside-explicit-transaction.sql index b68cbd8f..efebf3ba 100644 --- a/test/migrations/no-txn-partial-application-error-on-commit/2001-01-01-00-00-00-insert-duplicate-inside-explicit-transaction.sql +++ b/test/migrations/no-txn-partial-application-error-on-commit/2001-01-01-00-00-00-insert-duplicate-inside-explicit-transaction.sql @@ -8,12 +8,14 @@ COPY somedata FROM STDIN WITH (FORMAT csv); 6 \. +\restrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ -- Another comment SELECT 7; BEGIN; SELECT 3; CREATE TABLE othertablenotexists(); +\unrestrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ -- Yet another one COPY somedata FROM STDIN WITH (FORMAT csv); 1 diff --git a/test/migrations/no-txn-partial-application-error-outside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql b/test/migrations/no-txn-partial-application-error-outside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql index 2c7d4e59..d596a141 100644 --- a/test/migrations/no-txn-partial-application-error-outside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql +++ b/test/migrations/no-txn-partial-application-error-outside-txn/2000-01-01-00-00-00-create-table-with-unique-id.sql @@ -1,8 +1,10 @@ -- codd: no-txn CREATE TABLE somedata (id INT NOT NULL, UNIQUE(id)); +\restrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ COPY somedata FROM STDIN WITH (FORMAT csv); 1 2 3 \. +\unrestrict TAXaYefQ7OaPsbhTIwM0eA6r8S102Jqiy0mRQfQXQQmIdA9fqI7q4LFmKpchNqQ