diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7271dfcf8a..28dcf56fd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,6 @@ on: jobs: test: strategy: - max-parallel: 2 matrix: os: [ubuntu-latest, macos-latest, windows-latest] cgo: ['1', '0'] @@ -49,12 +48,25 @@ jobs: env: CGO_ENABLED: ${{ matrix.cgo }} + # Start a PostgreSQL server + - uses: sqlc-dev/action-setup-postgres@master + with: + postgres-version: "16" + id: postgres + + # Start a MySQL server + - uses: shogo82148/actions-setup-mysql@v1 + with: + mysql-version: "8.1" + - name: test ./... run: gotestsum --junitfile junit.xml -- --tags=examples -timeout 20m ./... env: CI_SQLC_PROJECT_ID: ${{ secrets.CI_SQLC_PROJECT_ID }} CI_SQLC_AUTH_TOKEN: ${{ secrets.CI_SQLC_AUTH_TOKEN }} SQLC_AUTH_TOKEN: ${{ secrets.CI_SQLC_AUTH_TOKEN }} + MYSQL_SERVER_URI: root:@tcp(localhost:3306)/mysql?multiStatements=true&parseTime=true + POSTGRESQL_SERVER_URI: ${{ steps.postgres.outputs.connection-uri }}?sslmode=disable CGO_ENABLED: ${{ matrix.cgo }} vuln_check: diff --git a/examples/authors/postgresql/db_test.go b/examples/authors/postgresql/db_test.go index bdcd90a547..53c28a4f32 100644 --- a/examples/authors/postgresql/db_test.go +++ b/examples/authors/postgresql/db_test.go @@ -10,12 +10,12 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" - "github.com/sqlc-dev/sqlc/internal/sqltest/hosted" + "github.com/sqlc-dev/sqlc/internal/sqltest/local" ) func TestAuthors(t *testing.T) { ctx := context.Background() - uri := hosted.PostgreSQL(t, []string{"schema.sql"}) + uri := local.PostgreSQL(t, []string{"schema.sql"}) db, err := pgx.Connect(ctx, uri) if err != nil { t.Fatal(err) diff --git a/examples/batch/postgresql/db_test.go b/examples/batch/postgresql/db_test.go index c39bd0b5ed..08c6f47ba5 100644 --- a/examples/batch/postgresql/db_test.go +++ b/examples/batch/postgresql/db_test.go @@ -10,11 +10,12 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" - "github.com/sqlc-dev/sqlc/internal/sqltest/hosted" + + "github.com/sqlc-dev/sqlc/internal/sqltest/local" ) func TestBatchBooks(t *testing.T) { - uri := hosted.PostgreSQL(t, []string{"schema.sql"}) + uri := local.PostgreSQL(t, []string{"schema.sql"}) ctx := context.Background() diff --git a/examples/booktest/postgresql/db_test.go b/examples/booktest/postgresql/db_test.go index 8eeb10518c..e33ee1b602 100644 --- a/examples/booktest/postgresql/db_test.go +++ b/examples/booktest/postgresql/db_test.go @@ -11,12 +11,12 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" - "github.com/sqlc-dev/sqlc/internal/sqltest/hosted" + "github.com/sqlc-dev/sqlc/internal/sqltest/local" ) func TestBooks(t *testing.T) { ctx := context.Background() - uri := hosted.PostgreSQL(t, []string{"schema.sql"}) + uri := local.PostgreSQL(t, []string{"schema.sql"}) db, err := pgx.Connect(ctx, uri) if err != nil { t.Fatal(err) diff --git a/examples/ondeck/mysql/db_test.go b/examples/ondeck/mysql/db_test.go index ffa63d9436..d05f835b23 100644 --- a/examples/ondeck/mysql/db_test.go +++ b/examples/ondeck/mysql/db_test.go @@ -12,7 +12,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/google/go-cmp/cmp" - "github.com/sqlc-dev/sqlc/internal/sqltest/hosted" + "github.com/sqlc-dev/sqlc/internal/sqltest/local" ) func join(vals ...string) sql.NullString { @@ -144,10 +144,10 @@ func runOnDeckQueries(t *testing.T, q *Queries) { func TestPrepared(t *testing.T) { t.Parallel() - uri := hosted.MySQL(t, []string{"schema"}) + uri := local.MySQL(t, []string{"schema"}) db, err := sql.Open("mysql", uri) if err != nil { - t.Fatal(err) + t.Fatalf("%s: %s", uri, err) } defer db.Close() @@ -162,10 +162,10 @@ func TestPrepared(t *testing.T) { func TestQueries(t *testing.T) { t.Parallel() - uri := hosted.MySQL(t, []string{"schema"}) + uri := local.MySQL(t, []string{"schema"}) db, err := sql.Open("mysql", uri) if err != nil { - t.Fatal(err) + t.Fatalf("%s: %s", uri, err) } defer db.Close() diff --git a/examples/ondeck/mysql/schema/0001_city.sql b/examples/ondeck/mysql/schema/0001_city.sql index 6be35d16bf..94e9b0f8d4 100644 --- a/examples/ondeck/mysql/schema/0001_city.sql +++ b/examples/ondeck/mysql/schema/0001_city.sql @@ -1,4 +1,4 @@ CREATE TABLE city ( slug varchar(255) PRIMARY KEY, name text NOT NULL -) +); diff --git a/examples/ondeck/postgresql/db_test.go b/examples/ondeck/postgresql/db_test.go index c4e4ce8bbf..570ddc891f 100644 --- a/examples/ondeck/postgresql/db_test.go +++ b/examples/ondeck/postgresql/db_test.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-cmp/cmp" _ "github.com/lib/pq" - "github.com/sqlc-dev/sqlc/internal/sqltest/hosted" + "github.com/sqlc-dev/sqlc/internal/sqltest/local" ) func runOnDeckQueries(t *testing.T, q *Queries) { @@ -126,10 +126,10 @@ func runOnDeckQueries(t *testing.T, q *Queries) { func TestPrepared(t *testing.T) { t.Parallel() - uri := hosted.PostgreSQL(t, []string{"schema"}) + uri := local.PostgreSQL(t, []string{"schema"}) db, err := sql.Open("postgres", uri) if err != nil { - t.Fatal(err) + t.Fatalf("%s: %s", uri, err) } defer db.Close() @@ -144,10 +144,10 @@ func TestPrepared(t *testing.T) { func TestQueries(t *testing.T) { t.Parallel() - uri := hosted.PostgreSQL(t, []string{"schema"}) + uri := local.PostgreSQL(t, []string{"schema"}) db, err := sql.Open("postgres", uri) if err != nil { - t.Fatal(err) + t.Fatalf("%s: %s", uri, err) } defer db.Close() diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go index 475d55ae09..560e112af2 100644 --- a/internal/codegen/golang/result.go +++ b/internal/codegen/golang/result.go @@ -1,6 +1,7 @@ package golang import ( + "bufio" "fmt" "sort" "strings" @@ -205,9 +206,14 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, structs [] comments = append(comments, query.Name) } comments = append(comments, " ") - for _, line := range strings.Split(query.Text, "\n") { + scanner := bufio.NewScanner(strings.NewReader(query.Text)) + for scanner.Scan() { + line := scanner.Text() comments = append(comments, " "+line) } + if err := scanner.Err(); err != nil { + return nil, err + } } gq := Query{ diff --git a/internal/endtoend/case_test.go b/internal/endtoend/case_test.go index 50dcc57ec5..8299647745 100644 --- a/internal/endtoend/case_test.go +++ b/internal/endtoend/case_test.go @@ -17,12 +17,17 @@ type Testcase struct { Exec *Exec } +type ExecMeta struct { + InvalidSchema bool `json:"invalid_schema"` +} + type Exec struct { Command string `json:"command"` Contexts []string `json:"contexts"` Process string `json:"process"` OS []string `json:"os"` Env map[string]string `json:"env"` + Meta ExecMeta `json:"meta"` } func parseStderr(t *testing.T, dir, testctx string) []byte { @@ -52,10 +57,10 @@ func parseExec(t *testing.T, dir string) *Exec { var e Exec blob, err := os.ReadFile(path) if err != nil { - t.Fatal(err) + t.Fatalf("%s: %s", path, err) } if err := json.Unmarshal(blob, &e); err != nil { - t.Fatal(err) + t.Fatalf("%s: %s", path, err) } if e.Command == "" { e.Command = "generate" diff --git a/internal/endtoend/ddl_test.go b/internal/endtoend/ddl_test.go index 71ea2052c9..bed9333743 100644 --- a/internal/endtoend/ddl_test.go +++ b/internal/endtoend/ddl_test.go @@ -1,51 +1,21 @@ package main import ( - "context" "fmt" "os" "path/filepath" - "runtime" - "slices" - "strings" "testing" - "github.com/jackc/pgx/v5" - "github.com/sqlc-dev/sqlc/internal/config" - "github.com/sqlc-dev/sqlc/internal/migrations" - "github.com/sqlc-dev/sqlc/internal/quickdb" - pb "github.com/sqlc-dev/sqlc/internal/quickdb/v1" - "github.com/sqlc-dev/sqlc/internal/sql/sqlpath" + "github.com/sqlc-dev/sqlc/internal/sqltest/local" ) func TestValidSchema(t *testing.T) { - if os.Getenv("CI") != "" && runtime.GOOS != "linux" { - t.Skipf("only run these tests in CI on linux: %s %s", os.Getenv("CI"), runtime.GOOS) - } - - ctx := context.Background() - - projectID := os.Getenv("CI_SQLC_PROJECT_ID") - authToken := os.Getenv("CI_SQLC_AUTH_TOKEN") - if projectID == "" || authToken == "" { - t.Skip("missing project id or auth token") - } - - client, err := quickdb.NewClient(projectID, authToken) - if err != nil { - t.Fatal(err) - } - - for _, replay := range FindTests(t, "testdata", "managed-db") { + for _, replay := range FindTests(t, "testdata", "base") { replay := replay // https://golang.org/doc/faq#closures_and_goroutines - if len(replay.Stderr) > 0 { - continue - } - if replay.Exec != nil { - if !slices.Contains(replay.Exec.Contexts, "managed-db") { + if replay.Exec.Meta.InvalidSchema { continue } } @@ -63,60 +33,28 @@ func TestValidSchema(t *testing.T) { for j, pkg := range conf.SQL { j, pkg := j, pkg - if pkg.Engine != config.EnginePostgreSQL { + switch pkg.Engine { + case config.EnginePostgreSQL: + // pass + case config.EngineMySQL: + // pass + default: continue } t.Run(fmt.Sprintf("endtoend-%s-%d", file, j), func(t *testing.T) { t.Parallel() - if strings.Contains(file, "pg_dump") { - t.Skip("loading pg_dump not supported") - } - var schema []string for _, path := range pkg.Schema { schema = append(schema, filepath.Join(filepath.Dir(file), path)) } - files, err := sqlpath.Glob(schema) - if err != nil { - t.Fatal(err) - } - - var sqls []string - for _, f := range files { - contents, err := os.ReadFile(f) - if err != nil { - t.Fatalf("%s: %s", f, err) - } - // Support loading pg_dump SQL files - before := strings.ReplaceAll(string(contents), "CREATE SCHEMA public;", "CREATE SCHEMA IF NOT EXISTS public;") - sqls = append(sqls, migrations.RemoveRollbackStatements(before)) - } - - resp, err := client.CreateEphemeralDatabase(ctx, &pb.CreateEphemeralDatabaseRequest{ - Engine: "postgresql", - Region: quickdb.GetClosestRegion(), - Migrations: sqls, - }) - if err != nil { - t.Fatalf("region %s: %s", quickdb.GetClosestRegion(), err) - } - - t.Cleanup(func() { - _, err = client.DropEphemeralDatabase(ctx, &pb.DropEphemeralDatabaseRequest{ - DatabaseId: resp.DatabaseId, - }) - if err != nil { - t.Fatal(err) - } - }) - - conn, err := pgx.Connect(ctx, resp.Uri) - if err != nil { - t.Fatalf("connect %s: %s", resp.Uri, err) + switch pkg.Engine { + case config.EnginePostgreSQL: + local.PostgreSQL(t, schema) + case config.EngineMySQL: + local.MySQL(t, schema) } - defer conn.Close(ctx) }) } } diff --git a/internal/endtoend/endtoend_test.go b/internal/endtoend/endtoend_test.go index 5753ce6d3a..8420340d80 100644 --- a/internal/endtoend/endtoend_test.go +++ b/internal/endtoend/endtoend_test.go @@ -17,6 +17,7 @@ import ( "github.com/sqlc-dev/sqlc/internal/cmd" "github.com/sqlc-dev/sqlc/internal/config" "github.com/sqlc-dev/sqlc/internal/opts" + "github.com/sqlc-dev/sqlc/internal/sqltest/local" ) func lineEndings() cmp.Option { @@ -99,7 +100,7 @@ func BenchmarkExamples(b *testing.B) { } type textContext struct { - Mutate func(*config.Config) + Mutate func(*testing.T, string) func(*config.Config) Enabled func() bool } @@ -113,15 +114,34 @@ func TestReplay(t *testing.T) { contexts := map[string]textContext{ "base": { - Mutate: func(c *config.Config) {}, + Mutate: func(t *testing.T, path string) func(*config.Config) { return func(c *config.Config) {} }, Enabled: func() bool { return true }, }, "managed-db": { - Mutate: func(c *config.Config) { - c.Cloud.Project = "01HAQMMECEYQYKFJN8MP16QC41" // TODO: Read from environment - for i := range c.SQL { - c.SQL[i].Database = &config.Database{ - Managed: true, + Mutate: func(t *testing.T, path string) func(*config.Config) { + return func(c *config.Config) { + c.Cloud.Project = "01HAQMMECEYQYKFJN8MP16QC41" // TODO: Read from environment + for i := range c.SQL { + files := []string{} + for _, s := range c.SQL[i].Schema { + files = append(files, filepath.Join(path, s)) + } + switch c.SQL[i].Engine { + case config.EnginePostgreSQL: + uri := local.PostgreSQL(t, files) + c.SQL[i].Database = &config.Database{ + URI: uri, + } + // case config.EngineMySQL: + // uri := local.MySQL(t, files) + // c.SQL[i].Database = &config.Database{ + // URI: uri, + // } + default: + c.SQL[i].Database = &config.Database{ + Managed: true, + } + } } } }, @@ -130,10 +150,12 @@ func TestReplay(t *testing.T) { if len(os.Getenv("SQLC_AUTH_TOKEN")) == 0 { return false } - // In CI, only run these tests from Linux - if os.Getenv("CI") != "" { - return runtime.GOOS == "linux" + if len(os.Getenv("POSTGRESQL_SERVER_URI")) == 0 { + return false } + // if len(os.Getenv("MYSQL_SERVER_URI")) == 0 { + // return false + // } return true }, }, @@ -188,7 +210,7 @@ func TestReplay(t *testing.T) { NoRemote: true, }, Stderr: &stderr, - MutateConfig: testctx.Mutate, + MutateConfig: testctx.Mutate(t, path), } switch args.Command { diff --git a/internal/endtoend/testdata/case_named_params/mysql/schema.sql b/internal/endtoend/testdata/case_named_params/mysql/schema.sql index 3053802224..5e2bcfacf0 100644 --- a/internal/endtoend/testdata/case_named_params/mysql/schema.sql +++ b/internal/endtoend/testdata/case_named_params/mysql/schema.sql @@ -2,8 +2,8 @@ CREATE TABLE authors ( id BIGINT PRIMARY KEY, - username TEXT NULL, - email TEXT NULL, + username VARCHAR(10) NULL, + email VARCHAR(10) NULL, name TEXT NOT NULL, bio TEXT, UNIQUE KEY idx_username (username), diff --git a/internal/endtoend/testdata/coalesce_params/mysql/exec.json b/internal/endtoend/testdata/coalesce_params/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/coalesce_params/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/coalesce_params/mysql/schema.sql b/internal/endtoend/testdata/coalesce_params/mysql/schema.sql index a0d41cbe74..0b1b34685c 100644 --- a/internal/endtoend/testdata/coalesce_params/mysql/schema.sql +++ b/internal/endtoend/testdata/coalesce_params/mysql/schema.sql @@ -14,7 +14,7 @@ CREATE TABLE `Calendar` ( KEY `Relation` (`Relation`), KEY `UniqueKey` (`UniqueKey`), KEY `IdKey` (`IdKey`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `Event` ( @@ -32,7 +32,7 @@ CREATE TABLE `Event` ( KEY `CalendarReference` (`CalendarReference`), KEY `UniqueKey` (`UniqueKey`), KEY `IdKey` (`IdKey`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE authors ( diff --git a/internal/endtoend/testdata/ddl_alter_table_drop_column_if_exists/mysql/exec.json b/internal/endtoend/testdata/ddl_alter_table_drop_column_if_exists/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_table_drop_column_if_exists/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/ddl_alter_table_drop_constraint/mysql/exec.json b/internal/endtoend/testdata/ddl_alter_table_drop_constraint/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_table_drop_constraint/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/ddl_create_func_exists/exec.json b/internal/endtoend/testdata/ddl_create_func_exists/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/ddl_create_func_exists/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/ddl_create_table_invalid_inherits/postgresql/stdlib/exec.json b/internal/endtoend/testdata/ddl_create_table_invalid_inherits/postgresql/stdlib/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/ddl_create_table_invalid_inherits/postgresql/stdlib/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/ddl_create_table_unknown_type/postgresql/pgx/exec.json b/internal/endtoend/testdata/ddl_create_table_unknown_type/postgresql/pgx/exec.json index ee1b7ecd9e..abd3ab549c 100644 --- a/internal/endtoend/testdata/ddl_create_table_unknown_type/postgresql/pgx/exec.json +++ b/internal/endtoend/testdata/ddl_create_table_unknown_type/postgresql/pgx/exec.json @@ -1,3 +1,6 @@ { - "contexts": ["managed-db"] + "contexts": ["unknown"], + "meta": { + "invalid_schema": true + } } diff --git a/internal/endtoend/testdata/ddl_drop_schema/mysql/exec.json b/internal/endtoend/testdata/ddl_drop_schema/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/ddl_drop_schema/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/emit_sql_as_comment/stdlib/exec.json b/internal/endtoend/testdata/emit_sql_as_comment/stdlib/exec.json new file mode 100644 index 0000000000..9f208fb2df --- /dev/null +++ b/internal/endtoend/testdata/emit_sql_as_comment/stdlib/exec.json @@ -0,0 +1,3 @@ +{ + "os": ["darwin", "linux"] +} diff --git a/internal/endtoend/testdata/insert_select_invalid/mysql/query.sql b/internal/endtoend/testdata/insert_select_invalid/mysql/query.sql index cfd90fe55d..3311b32009 100644 --- a/internal/endtoend/testdata/insert_select_invalid/mysql/query.sql +++ b/internal/endtoend/testdata/insert_select_invalid/mysql/query.sql @@ -1,5 +1,3 @@ -CREATE TABLE foo (bar text); - -- name: InsertFoo :exec INSERT INTO foo (bar) SELECT 1, ?, ?; diff --git a/internal/endtoend/testdata/insert_select_invalid/mysql/schema.sql b/internal/endtoend/testdata/insert_select_invalid/mysql/schema.sql new file mode 100644 index 0000000000..d849628fb1 --- /dev/null +++ b/internal/endtoend/testdata/insert_select_invalid/mysql/schema.sql @@ -0,0 +1 @@ +CREATE TABLE foo (bar text); diff --git a/internal/endtoend/testdata/insert_select_invalid/mysql/sqlc.json b/internal/endtoend/testdata/insert_select_invalid/mysql/sqlc.json index 0657f4db83..974aa9ff9e 100644 --- a/internal/endtoend/testdata/insert_select_invalid/mysql/sqlc.json +++ b/internal/endtoend/testdata/insert_select_invalid/mysql/sqlc.json @@ -5,7 +5,7 @@ "engine": "mysql", "path": "go", "name": "querytest", - "schema": "query.sql", + "schema": "schema.sql", "queries": "query.sql" } ] diff --git a/internal/endtoend/testdata/insert_select_invalid/mysql/stderr.txt b/internal/endtoend/testdata/insert_select_invalid/mysql/stderr.txt index 063b2a149a..91d0b8a06c 100644 --- a/internal/endtoend/testdata/insert_select_invalid/mysql/stderr.txt +++ b/internal/endtoend/testdata/insert_select_invalid/mysql/stderr.txt @@ -1,2 +1,2 @@ # package querytest -query.sql:4:1: INSERT has more expressions than target columns +query.sql:1:1: INSERT has more expressions than target columns diff --git a/internal/endtoend/testdata/invalid_group_by_reference/mysql/exec.json b/internal/endtoend/testdata/invalid_group_by_reference/mysql/exec.json new file mode 100644 index 0000000000..0775566a14 --- /dev/null +++ b/internal/endtoend/testdata/invalid_group_by_reference/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "meta": { + "invalid_schema": true + } +} + diff --git a/internal/endtoend/testdata/invalid_named_params/mysql/query.sql b/internal/endtoend/testdata/invalid_named_params/mysql/query.sql index 1ea4a9e5dc..349d2c2ccb 100644 --- a/internal/endtoend/testdata/invalid_named_params/mysql/query.sql +++ b/internal/endtoend/testdata/invalid_named_params/mysql/query.sql @@ -1,8 +1,3 @@ -CREATE TABLE authors ( - id BIGINT PRIMARY KEY, - bio TEXT -); - -- name: ListAuthors :one SELECT * FROM authors diff --git a/internal/endtoend/testdata/invalid_named_params/mysql/schema.sql b/internal/endtoend/testdata/invalid_named_params/mysql/schema.sql new file mode 100644 index 0000000000..9804a5cd1b --- /dev/null +++ b/internal/endtoend/testdata/invalid_named_params/mysql/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE authors ( + id BIGINT PRIMARY KEY, + bio TEXT +); diff --git a/internal/endtoend/testdata/invalid_named_params/mysql/sqlc.json b/internal/endtoend/testdata/invalid_named_params/mysql/sqlc.json index 534b7e24e9..0390f67889 100644 --- a/internal/endtoend/testdata/invalid_named_params/mysql/sqlc.json +++ b/internal/endtoend/testdata/invalid_named_params/mysql/sqlc.json @@ -5,7 +5,7 @@ "path": "go", "engine": "mysql", "name": "querytest", - "schema": "query.sql", + "schema": "schema.sql", "queries": "query.sql" } ] diff --git a/internal/endtoend/testdata/invalid_table_alias/mysql/query.sql b/internal/endtoend/testdata/invalid_table_alias/mysql/query.sql index 22482fb724..52f5aae051 100644 --- a/internal/endtoend/testdata/invalid_table_alias/mysql/query.sql +++ b/internal/endtoend/testdata/invalid_table_alias/mysql/query.sql @@ -1,10 +1,3 @@ --- https://github.com/sqlc-dev/sqlc/issues/437 -CREATE TABLE authors ( - id INT PRIMARY KEY, - name VARCHAR(255) NOT NULL, - bio text -); - -- name: GetAuthor :one SELECT * FROM authors a diff --git a/internal/endtoend/testdata/invalid_table_alias/mysql/schema.sql b/internal/endtoend/testdata/invalid_table_alias/mysql/schema.sql new file mode 100644 index 0000000000..ee27f30abe --- /dev/null +++ b/internal/endtoend/testdata/invalid_table_alias/mysql/schema.sql @@ -0,0 +1,7 @@ +-- https://github.com/sqlc-dev/sqlc/issues/437 +CREATE TABLE authors ( + id INT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + bio text +); + diff --git a/internal/endtoend/testdata/invalid_table_alias/mysql/sqlc.json b/internal/endtoend/testdata/invalid_table_alias/mysql/sqlc.json index 534b7e24e9..0390f67889 100644 --- a/internal/endtoend/testdata/invalid_table_alias/mysql/sqlc.json +++ b/internal/endtoend/testdata/invalid_table_alias/mysql/sqlc.json @@ -5,7 +5,7 @@ "path": "go", "engine": "mysql", "name": "querytest", - "schema": "query.sql", + "schema": "schema.sql", "queries": "query.sql" } ] diff --git a/internal/endtoend/testdata/invalid_table_alias/mysql/stderr.txt b/internal/endtoend/testdata/invalid_table_alias/mysql/stderr.txt index 810c893a70..1eddeaac99 100644 --- a/internal/endtoend/testdata/invalid_table_alias/mysql/stderr.txt +++ b/internal/endtoend/testdata/invalid_table_alias/mysql/stderr.txt @@ -1,2 +1,2 @@ # package querytest -query.sql:11:9: table alias "p" does not exist +query.sql:4:9: table alias "p" does not exist diff --git a/internal/endtoend/testdata/invalid_table_alias/postgresql/exec.json b/internal/endtoend/testdata/invalid_table_alias/postgresql/exec.json new file mode 100644 index 0000000000..9f208fb2df --- /dev/null +++ b/internal/endtoend/testdata/invalid_table_alias/postgresql/exec.json @@ -0,0 +1,3 @@ +{ + "os": ["darwin", "linux"] +} diff --git a/internal/endtoend/testdata/join_left/mysql/schema.sql b/internal/endtoend/testdata/join_left/mysql/schema.sql index a73a02ca00..9b15eaea36 100644 --- a/internal/endtoend/testdata/join_left/mysql/schema.sql +++ b/internal/endtoend/testdata/join_left/mysql/schema.sql @@ -28,11 +28,11 @@ CREATE TABLE super_authors ( CREATE TABLE users_2 ( user_id INT PRIMARY KEY, user_nickname VARCHAR(30) UNIQUE NOT NULL, - user_email TEXT UNIQUE NOT NULL, + user_email VARCHAR(20) UNIQUE NOT NULL, user_display_name TEXT NOT NULL, user_password TEXT NULL, - user_google_id TEXT UNIQUE NULL, - user_apple_id TEXT UNIQUE NULL, + user_google_id VARCHAR(20) UNIQUE NULL, + user_apple_id VARCHAR(20) UNIQUE NULL, user_bio VARCHAR(160) NOT NULL DEFAULT '', user_created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, user_avatar_id INT UNIQUE NULL diff --git a/internal/endtoend/testdata/join_table_name/mysql/go/models.go b/internal/endtoend/testdata/join_table_name/mysql/go/models.go index 33a3b8dacc..4b1645d290 100644 --- a/internal/endtoend/testdata/join_table_name/mysql/go/models.go +++ b/internal/endtoend/testdata/join_table_name/mysql/go/models.go @@ -4,7 +4,9 @@ package querytest -import () +import ( + "database/sql" +) type Bar struct { ID uint64 @@ -12,5 +14,5 @@ type Bar struct { type Foo struct { ID uint64 - Bar uint64 + Bar sql.NullInt32 } diff --git a/internal/endtoend/testdata/join_table_name/mysql/schema.sql b/internal/endtoend/testdata/join_table_name/mysql/schema.sql index b0f5adf952..c6254b343e 100644 --- a/internal/endtoend/testdata/join_table_name/mysql/schema.sql +++ b/internal/endtoend/testdata/join_table_name/mysql/schema.sql @@ -1,3 +1,3 @@ CREATE TABLE bar (id serial not null); -CREATE TABLE foo (id serial not null, bar serial references bar(id)); +CREATE TABLE foo (id serial not null, bar integer references bar(id)); diff --git a/internal/endtoend/testdata/join_two_tables/mysql/go/models.go b/internal/endtoend/testdata/join_two_tables/mysql/go/models.go index 84d8484564..f834ab048e 100644 --- a/internal/endtoend/testdata/join_two_tables/mysql/go/models.go +++ b/internal/endtoend/testdata/join_two_tables/mysql/go/models.go @@ -16,5 +16,5 @@ type Baz struct { type Foo struct { BarID uint64 - BazID uint64 + BazID int32 } diff --git a/internal/endtoend/testdata/join_two_tables/mysql/schema.sql b/internal/endtoend/testdata/join_two_tables/mysql/schema.sql index 86eb87a1ff..d8b5e500cf 100644 --- a/internal/endtoend/testdata/join_two_tables/mysql/schema.sql +++ b/internal/endtoend/testdata/join_two_tables/mysql/schema.sql @@ -1,4 +1,4 @@ -CREATE TABLE foo (bar_id serial not null, baz_id serial not null); +CREATE TABLE foo (bar_id serial not null, baz_id integer not null); CREATE TABLE bar (id serial not null); CREATE TABLE baz (id serial not null); diff --git a/internal/endtoend/testdata/mix_param_types/mysql/schema.sql b/internal/endtoend/testdata/mix_param_types/mysql/schema.sql new file mode 100644 index 0000000000..b515bf8d04 --- /dev/null +++ b/internal/endtoend/testdata/mix_param_types/mysql/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + id serial not null, + name text not null, + phone text not null +); diff --git a/internal/endtoend/testdata/mix_param_types/mysql/sqlc.json b/internal/endtoend/testdata/mix_param_types/mysql/sqlc.json index 145f64ba3f..3e8d09d00d 100644 --- a/internal/endtoend/testdata/mix_param_types/mysql/sqlc.json +++ b/internal/endtoend/testdata/mix_param_types/mysql/sqlc.json @@ -4,7 +4,7 @@ { "path": "go", "name": "querytest", - "schema": "test.sql", + "schema": "schema.sql", "queries": "test.sql", "engine": "mysql" } diff --git a/internal/endtoend/testdata/mix_param_types/mysql/test.sql b/internal/endtoend/testdata/mix_param_types/mysql/test.sql index b624d3e2ea..e23f1dcb70 100644 --- a/internal/endtoend/testdata/mix_param_types/mysql/test.sql +++ b/internal/endtoend/testdata/mix_param_types/mysql/test.sql @@ -1,9 +1,3 @@ -CREATE TABLE bar ( - id serial not null, - name text not null, - phone text not null -); - -- name: CountOne :one SELECT count(1) FROM bar WHERE id = sqlc.arg(id) AND name <> ?; diff --git a/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/go/models.go b/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/go/models.go index f10e6f395c..759354e26e 100644 --- a/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/go/models.go +++ b/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/go/models.go @@ -10,5 +10,5 @@ import ( type Student struct { StudentName sql.NullString - Score sql.NullFloat64 + TestScore sql.NullFloat64 } diff --git a/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/group_concat.sql b/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/group_concat.sql index f1858e42fd..443253851d 100644 --- a/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/group_concat.sql +++ b/internal/endtoend/testdata/mysql_reference_manual/aggregate_functions/group_concat.sql @@ -1,8 +1,3 @@ -CREATE TABLE student ( - student_name VARCHAR(255), - score DOUBLE -); - -- name: GroupConcat :many SELECT student_name, GROUP_CONCAT(test_score) FROM student diff --git a/internal/endtoend/testdata/mysql_reference_manual/date_and_time_functions/go/models.go b/internal/endtoend/testdata/mysql_reference_manual/date_and_time_functions/go/models.go index 5d14d70322..49038cc7f4 100644 --- a/internal/endtoend/testdata/mysql_reference_manual/date_and_time_functions/go/models.go +++ b/internal/endtoend/testdata/mysql_reference_manual/date_and_time_functions/go/models.go @@ -4,4 +4,11 @@ package date_and_time_functions -import () +import ( + "database/sql" +) + +type Student struct { + StudentName sql.NullString + TestScore sql.NullFloat64 +} diff --git a/internal/endtoend/testdata/mysql_reference_manual/schema.sql b/internal/endtoend/testdata/mysql_reference_manual/schema.sql new file mode 100644 index 0000000000..5dcef48427 --- /dev/null +++ b/internal/endtoend/testdata/mysql_reference_manual/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE student ( + student_name VARCHAR(255), + test_score DOUBLE +); diff --git a/internal/endtoend/testdata/mysql_reference_manual/sqlc.json b/internal/endtoend/testdata/mysql_reference_manual/sqlc.json index 26e8643999..71c8ff5aa8 100644 --- a/internal/endtoend/testdata/mysql_reference_manual/sqlc.json +++ b/internal/endtoend/testdata/mysql_reference_manual/sqlc.json @@ -4,14 +4,14 @@ { "name": "date_and_time_functions", "path": "date_and_time_functions/go", - "schema": "date_and_time_functions", + "schema": "schema.sql", "queries": "date_and_time_functions", "engine": "mysql" }, { "name": "aggregate_functions", "path": "aggregate_functions/go", - "schema": "aggregate_functions", + "schema": "schema.sql", "queries": "aggregate_functions", "engine": "mysql" } diff --git a/internal/endtoend/testdata/pg_dump/exec.json b/internal/endtoend/testdata/pg_dump/exec.json index c16f123ce3..c85ca2dfa7 100644 --- a/internal/endtoend/testdata/pg_dump/exec.json +++ b/internal/endtoend/testdata/pg_dump/exec.json @@ -1,3 +1,6 @@ { - "contexts": ["base"] -} \ No newline at end of file + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/pg_vector/postgresql/pgx/exec.json b/internal/endtoend/testdata/pg_vector/postgresql/pgx/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/pg_vector/postgresql/pgx/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/schema_scoped_create/mysql/exec.json b/internal/endtoend/testdata/schema_scoped_create/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/schema_scoped_create/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/schema_scoped_delete/mysql/exec.json b/internal/endtoend/testdata/schema_scoped_delete/mysql/exec.json new file mode 100644 index 0000000000..a0e224b3f8 --- /dev/null +++ b/internal/endtoend/testdata/schema_scoped_delete/mysql/exec.json @@ -0,0 +1,7 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} + \ No newline at end of file diff --git a/internal/endtoend/testdata/schema_scoped_filter/mysql/exec.json b/internal/endtoend/testdata/schema_scoped_filter/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/schema_scoped_filter/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/schema_scoped_list/mysql/exec.json b/internal/endtoend/testdata/schema_scoped_list/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/schema_scoped_list/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/schema_scoped_update/mysql/exec.json b/internal/endtoend/testdata/schema_scoped_update/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/schema_scoped_update/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/select_empty_column_list/mysql/query.sql b/internal/endtoend/testdata/select_empty_column_list/mysql/query.sql index 81b6ff14a1..ae6cd0c9be 100644 --- a/internal/endtoend/testdata/select_empty_column_list/mysql/query.sql +++ b/internal/endtoend/testdata/select_empty_column_list/mysql/query.sql @@ -1,4 +1,2 @@ -CREATE TABLE bar (name text); - -- name: GetBars :many -SELECT FROM bar; \ No newline at end of file +SELECT FROM bar; diff --git a/internal/endtoend/testdata/select_empty_column_list/mysql/schema.sql b/internal/endtoend/testdata/select_empty_column_list/mysql/schema.sql new file mode 100644 index 0000000000..fb18b4b3a8 --- /dev/null +++ b/internal/endtoend/testdata/select_empty_column_list/mysql/schema.sql @@ -0,0 +1 @@ +CREATE TABLE bar (name text); diff --git a/internal/endtoend/testdata/select_empty_column_list/mysql/sqlc.json b/internal/endtoend/testdata/select_empty_column_list/mysql/sqlc.json index 445bbd1589..e41c39e8b3 100644 --- a/internal/endtoend/testdata/select_empty_column_list/mysql/sqlc.json +++ b/internal/endtoend/testdata/select_empty_column_list/mysql/sqlc.json @@ -5,7 +5,7 @@ "path": "go", "engine": "mysql", "name": "querytest", - "schema": "query.sql", + "schema": "schema.sql", "queries": "query.sql" } ] diff --git a/internal/endtoend/testdata/select_empty_column_list/mysql/stderr.txt b/internal/endtoend/testdata/select_empty_column_list/mysql/stderr.txt index 11aeb304b0..2744126cd0 100644 --- a/internal/endtoend/testdata/select_empty_column_list/mysql/stderr.txt +++ b/internal/endtoend/testdata/select_empty_column_list/mysql/stderr.txt @@ -1,2 +1,2 @@ # package querytest -query.sql:4:12: syntax error near "FROM bar;" " +query.sql:2:12: syntax error near "FROM bar;" diff --git a/internal/endtoend/testdata/single_param_conflict/mysql/schema.sql b/internal/endtoend/testdata/single_param_conflict/mysql/schema.sql index 376329e101..2f49d219f3 100644 --- a/internal/endtoend/testdata/single_param_conflict/mysql/schema.sql +++ b/internal/endtoend/testdata/single_param_conflict/mysql/schema.sql @@ -7,5 +7,5 @@ CREATE TABLE authors ( -- https://github.com/sqlc-dev/sqlc/issues/1290 CREATE TABLE users ( - sub TEXT PRIMARY KEY + sub VARCHAR(10) PRIMARY KEY ); diff --git a/internal/endtoend/testdata/sqlc_arg_invalid/mysql/query.sql b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/query.sql index 3e46d7204b..80d171b2c2 100644 --- a/internal/endtoend/testdata/sqlc_arg_invalid/mysql/query.sql +++ b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/query.sql @@ -1,8 +1,3 @@ -CREATE TABLE users ( - id serial, - first_name text not null -); - -- name: WrongFunc :one select id, first_name from users where id = sqlc.argh(target_id); diff --git a/internal/endtoend/testdata/sqlc_arg_invalid/mysql/schema.sql b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/schema.sql new file mode 100644 index 0000000000..b11bfef310 --- /dev/null +++ b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE users ( + id serial, + first_name text not null +); diff --git a/internal/endtoend/testdata/sqlc_arg_invalid/mysql/sqlc.json b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/sqlc.json index bfbd23e211..a9e7b055a4 100644 --- a/internal/endtoend/testdata/sqlc_arg_invalid/mysql/sqlc.json +++ b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/sqlc.json @@ -4,7 +4,7 @@ { "name": "querytest", "path": "go", - "schema": "query.sql", + "schema": "schema.sql", "queries": "query.sql", "engine": "mysql" } diff --git a/internal/endtoend/testdata/sqlc_arg_invalid/mysql/stderr.txt b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/stderr.txt index be38c8b505..73966182fb 100644 --- a/internal/endtoend/testdata/sqlc_arg_invalid/mysql/stderr.txt +++ b/internal/endtoend/testdata/sqlc_arg_invalid/mysql/stderr.txt @@ -1,6 +1,6 @@ # package querytest -query.sql:7:1: function "sqlc.argh" does not exist -query.sql:10:45: expected 1 parameter to sqlc.arg; got 2 -query.sql:13:45: expected 1 parameter to sqlc.arg; got 0 -query.sql:16:45: expected parameter to sqlc.arg to be string or reference; got *ast.FuncCall -query.sql:19:45: expected parameter to sqlc.arg to be string or reference; got *ast.ParamRef +query.sql:1:1: function "sqlc.argh" does not exist +query.sql:5:45: expected 1 parameter to sqlc.arg; got 2 +query.sql:8:45: expected 1 parameter to sqlc.arg; got 0 +query.sql:11:45: expected parameter to sqlc.arg to be string or reference; got *ast.FuncCall +query.sql:14:45: expected parameter to sqlc.arg to be string or reference; got *ast.ParamRef diff --git a/internal/endtoend/testdata/sqlc_embed/mysql/exec.json b/internal/endtoend/testdata/sqlc_embed/mysql/exec.json new file mode 100644 index 0000000000..c85ca2dfa7 --- /dev/null +++ b/internal/endtoend/testdata/sqlc_embed/mysql/exec.json @@ -0,0 +1,6 @@ +{ + "contexts": ["base"], + "meta": { + "invalid_schema": true + } +} diff --git a/internal/endtoend/testdata/valid_group_by_reference/mysql/schema.sql b/internal/endtoend/testdata/valid_group_by_reference/mysql/schema.sql index 69a38e0417..63bcff540c 100644 --- a/internal/endtoend/testdata/valid_group_by_reference/mysql/schema.sql +++ b/internal/endtoend/testdata/valid_group_by_reference/mysql/schema.sql @@ -1,6 +1,6 @@ CREATE TABLE authors ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, - name text NOT NULL, + name VARCHAR(10) NOT NULL, bio text, UNIQUE(name) ); diff --git a/internal/sqltest/local/id.go b/internal/sqltest/local/id.go new file mode 100644 index 0000000000..919e2dca1d --- /dev/null +++ b/internal/sqltest/local/id.go @@ -0,0 +1,13 @@ +package local + +import "math/rand" + +var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func id() string { + b := make([]rune, 10) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} diff --git a/internal/sqltest/local/mysql.go b/internal/sqltest/local/mysql.go new file mode 100644 index 0000000000..c61cee3418 --- /dev/null +++ b/internal/sqltest/local/mysql.go @@ -0,0 +1,92 @@ +package local + +import ( + "context" + "database/sql" + "fmt" + "os" + "strings" + "sync" + "testing" + + "github.com/go-sql-driver/mysql" + + migrate "github.com/sqlc-dev/sqlc/internal/migrations" + "github.com/sqlc-dev/sqlc/internal/sql/sqlpath" +) + +var mysqlSync sync.Once +var mysqlPool *sql.DB + +func MySQL(t *testing.T, migrations []string) string { + ctx := context.Background() + t.Helper() + + dburi := os.Getenv("MYSQL_SERVER_URI") + if dburi == "" { + t.Skip("MYSQL_SERVER_URI is empty") + } + + mysqlSync.Do(func() { + db, err := sql.Open("mysql", dburi) + if err != nil { + t.Fatal(err) + } + mysqlPool = db + }) + + if mysqlPool == nil { + t.Fatalf("MySQL pool creation failed") + } + + var seed []string + files, err := sqlpath.Glob(migrations) + if err != nil { + t.Fatal(err) + } + for _, f := range files { + blob, err := os.ReadFile(f) + if err != nil { + t.Fatal(err) + } + seed = append(seed, migrate.RemoveRollbackStatements(string(blob))) + } + + cfg, err := mysql.ParseDSN(dburi) + if err != nil { + t.Fatal(err) + } + + name := fmt.Sprintf("sqlc_test_%s", id()) + + if _, err := mysqlPool.ExecContext(ctx, fmt.Sprintf("CREATE DATABASE `%s`", name)); err != nil { + t.Fatal(err) + } + + cfg.DBName = name + + dropQuery := fmt.Sprintf("DROP DATABASE `%s`", name) + + t.Cleanup(func() { + if _, err := mysqlPool.ExecContext(ctx, dropQuery); err != nil { + t.Fatal(err) + } + }) + + db, err := sql.Open("mysql", cfg.FormatDSN()) + if err != nil { + t.Fatalf("connect %s: %s", name, err) + } + defer db.Close() + + for _, q := range seed { + if len(strings.TrimSpace(q)) == 0 { + continue + } + if _, err := db.ExecContext(ctx, q); err != nil { + t.Fatalf("%s: %s", q, err) + } + } + + return cfg.FormatDSN() +} diff --git a/internal/sqltest/local/postgres.go b/internal/sqltest/local/postgres.go new file mode 100644 index 0000000000..a283273ac5 --- /dev/null +++ b/internal/sqltest/local/postgres.go @@ -0,0 +1,92 @@ +package local + +import ( + "context" + "fmt" + "net/url" + "os" + "strings" + "sync" + "testing" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + + migrate "github.com/sqlc-dev/sqlc/internal/migrations" + "github.com/sqlc-dev/sqlc/internal/sql/sqlpath" +) + +var postgresPool *pgxpool.Pool +var postgresSync sync.Once + +func PostgreSQL(t *testing.T, migrations []string) string { + ctx := context.Background() + t.Helper() + + dburi := os.Getenv("POSTGRESQL_SERVER_URI") + if dburi == "" { + t.Skip("POSTGRESQL_SERVER_URI is empty") + } + + postgresSync.Do(func() { + pool, err := pgxpool.New(ctx, dburi) + if err != nil { + t.Fatal(err) + } + postgresPool = pool + }) + + if postgresPool == nil { + t.Fatalf("PostgreSQL pool creation failed") + } + + var seed []string + files, err := sqlpath.Glob(migrations) + if err != nil { + t.Fatal(err) + } + for _, f := range files { + blob, err := os.ReadFile(f) + if err != nil { + t.Fatal(err) + } + seed = append(seed, migrate.RemoveRollbackStatements(string(blob))) + } + + uri, err := url.Parse(dburi) + if err != nil { + t.Fatal(err) + } + + name := fmt.Sprintf("sqlc_test_%s", id()) + + if _, err := postgresPool.Exec(ctx, fmt.Sprintf(`CREATE DATABASE "%s"`, name)); err != nil { + t.Fatal(err) + } + + uri.Path = name + dropQuery := fmt.Sprintf(`DROP DATABASE IF EXISTS "%s" WITH (FORCE)`, name) + + t.Cleanup(func() { + if _, err := postgresPool.Exec(ctx, dropQuery); err != nil { + t.Fatal(err) + } + }) + + conn, err := pgx.Connect(ctx, uri.String()) + if err != nil { + t.Fatalf("connect %s: %s", name, err) + } + defer conn.Close(ctx) + + for _, q := range seed { + if len(strings.TrimSpace(q)) == 0 { + continue + } + if _, err := conn.Exec(ctx, q); err != nil { + t.Fatalf("%s: %s", q, err) + } + } + + return uri.String() +}