diff --git a/internal/compiler/compile.go b/internal/compiler/compile.go index 1a95b586f4..b6bba42e16 100644 --- a/internal/compiler/compile.go +++ b/internal/compiler/compile.go @@ -39,6 +39,7 @@ func (c *Compiler) parseCatalog(schemas []string) error { continue } contents := migrations.RemoveRollbackStatements(string(blob)) + contents = migrations.RemovePsqlMetaCommands(contents) c.schema = append(c.schema, contents) // In database-only mode, we parse the schema to validate syntax diff --git a/internal/endtoend/testdata/pg_dump/schema.sql b/internal/endtoend/testdata/pg_dump/schema.sql index 06bfb9d37c..ef1ab49c19 100644 --- a/internal/endtoend/testdata/pg_dump/schema.sql +++ b/internal/endtoend/testdata/pg_dump/schema.sql @@ -5,6 +5,8 @@ -- Dumped from database version 15.3 (Debian 15.3-1.pgdg120+1) -- Dumped by pg_dump version 15.3 +\restrict auwherpfqaiuwrhgp + SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; @@ -83,6 +85,8 @@ ALTER TABLE ONLY public.authors ADD CONSTRAINT authors_pkey PRIMARY KEY (id); +\unrestrict auwherpfqaiuwrhgp + -- -- PostgreSQL database dump complete -- diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go index 4ade6045a4..bd5da61ed5 100644 --- a/internal/migrations/migrations.go +++ b/internal/migrations/migrations.go @@ -2,9 +2,16 @@ package migrations import ( "bufio" + "regexp" "strings" ) +// psqlMetaCommand matches a psql meta-command (a line that begins with a +// backslash followed by a command name). pg_dump emits these starting with +// PostgreSQL 17.6 / 16.10 / 15.14 / 14.19 / 13.22 (e.g. `\restrict KEY` and +// `\unrestrict KEY`), and sqlc's SQL parsers cannot handle them. +var psqlMetaCommand = regexp.MustCompile(`^\\[A-Za-z!?;][^\n]*$`) + // Remove all lines after a rollback comment. // // goose: -- +goose Down @@ -33,6 +40,22 @@ func RemoveRollbackStatements(contents string) string { return strings.Join(lines, "\n") } +// RemovePsqlMetaCommands strips psql meta-command lines (e.g. `\restrict KEY`, +// `\unrestrict KEY`, `\connect foo`) from SQL input. These are emitted by +// pg_dump but are not valid SQL, so they must be removed before parsing. +func RemovePsqlMetaCommands(contents string) string { + s := bufio.NewScanner(strings.NewReader(contents)) + var lines []string + for s.Scan() { + line := s.Text() + if psqlMetaCommand.MatchString(line) { + continue + } + lines = append(lines, line) + } + return strings.Join(lines, "\n") +} + func IsDown(filename string) bool { // Remove golang-migrate rollback files. return strings.HasSuffix(filename, ".down.sql") diff --git a/internal/migrations/migrations_test.go b/internal/migrations/migrations_test.go index d987992582..1217674fdc 100644 --- a/internal/migrations/migrations_test.go +++ b/internal/migrations/migrations_test.go @@ -56,6 +56,17 @@ const outputDbmate = ` -- migrate:up CREATE TABLE foo (bar int);` +const inputPsqlMeta = `\restrict auwherpfqaiuwrhgp + +CREATE TABLE foo (id int); + +\unrestrict auwherpfqaiuwrhgp +` + +const outputPsqlMeta = ` +CREATE TABLE foo (id int); +` + func TestRemoveRollback(t *testing.T) { if diff := cmp.Diff(outputGoose, RemoveRollbackStatements(inputGoose)); diff != "" { t.Errorf("goose migration mismatch:\n%s", diff) @@ -71,6 +82,12 @@ func TestRemoveRollback(t *testing.T) { } } +func TestRemovePsqlMetaCommands(t *testing.T) { + if diff := cmp.Diff(outputPsqlMeta, RemovePsqlMetaCommands(inputPsqlMeta)); diff != "" { + t.Errorf("psql meta-command mismatch:\n%s", diff) + } +} + func TestRemoveGolangMigrateRollback(t *testing.T) { filenames := map[string]bool{ // make sure we let through golang-migrate files that aren't rollbacks