diff --git a/examples/ondeck/mysql/city.sql.go b/examples/ondeck/mysql/city.sql.go new file mode 100644 index 0000000000..f6ae1ea873 --- /dev/null +++ b/examples/ondeck/mysql/city.sql.go @@ -0,0 +1,86 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: city.sql + +package ondeck + +import ( + "context" + "database/sql" +) + +const createCity = `-- name: CreateCity :execresult +INSERT INTO city ( + name, + slug +) VALUES ( + ?, + ? +) +` + +type CreateCityParams struct { + Name string `json:"name"` + Slug string `json:"slug"` +} + +func (q *Queries) CreateCity(ctx context.Context, arg CreateCityParams) (sql.Result, error) { + return q.exec(ctx, q.createCityStmt, createCity, arg.Name, arg.Slug) +} + +const getCity = `-- name: GetCity :one +SELECT slug, name +FROM city +WHERE slug = ? +` + +func (q *Queries) GetCity(ctx context.Context, slug string) (City, error) { + row := q.queryRow(ctx, q.getCityStmt, getCity, slug) + var i City + err := row.Scan(&i.Slug, &i.Name) + return i, err +} + +const listCities = `-- name: ListCities :many +SELECT slug, name +FROM city +ORDER BY name +` + +func (q *Queries) ListCities(ctx context.Context) ([]City, error) { + rows, err := q.query(ctx, q.listCitiesStmt, listCities) + if err != nil { + return nil, err + } + defer rows.Close() + var items []City + for rows.Next() { + var i City + if err := rows.Scan(&i.Slug, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateCityName = `-- name: UpdateCityName :exec +UPDATE city +SET name = ? +WHERE slug = ? +` + +type UpdateCityNameParams struct { + Name string `json:"name"` + Slug string `json:"slug"` +} + +func (q *Queries) UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error { + _, err := q.exec(ctx, q.updateCityNameStmt, updateCityName, arg.Name, arg.Slug) + return err +} diff --git a/examples/ondeck/db.go b/examples/ondeck/mysql/db.go similarity index 100% rename from examples/ondeck/db.go rename to examples/ondeck/mysql/db.go diff --git a/examples/ondeck/mysql/db_test.go b/examples/ondeck/mysql/db_test.go new file mode 100644 index 0000000000..9d189f9477 --- /dev/null +++ b/examples/ondeck/mysql/db_test.go @@ -0,0 +1,163 @@ +// +build examples + +package ondeck + +import ( + "context" + "database/sql" + "strings" + "testing" + + "github.com/kyleconroy/sqlc/internal/sqltest" + + "github.com/google/go-cmp/cmp" +) + +func join(vals ...string) sql.NullString { + if len(vals) == 0 { + return sql.NullString{} + } + return sql.NullString{ + Valid: true, + String: strings.Join(vals, ","), + } +} + +func runOnDeckQueries(t *testing.T, q *Queries) { + ctx := context.Background() + + _, err := q.CreateCity(ctx, CreateCityParams{ + Slug: "san-francisco", + Name: "San Francisco", + }) + if err != nil { + t.Fatal(err) + } + + city, err := q.GetCity(ctx, "san-francisco") + if err != nil { + t.Fatal(err) + } + + venueResult, err := q.CreateVenue(ctx, CreateVenueParams{ + Slug: "the-fillmore", + Name: "The Fillmore", + City: city.Slug, + SpotifyPlaylist: "spotify:uri", + Status: StatusOpen, + Statuses: join(string(StatusOpen), string(StatusClosed)), + Tags: join("rock", "punk"), + }) + if err != nil { + t.Fatal(err) + } + venueID, err := venueResult.LastInsertId() + if err != nil { + t.Fatal(err) + } + + venue, err := q.GetVenue(ctx, GetVenueParams{ + Slug: "the-fillmore", + City: city.Slug, + }) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(venue.ID, venueID); diff != "" { + t.Errorf("venue ID mismatch:\n%s", diff) + } + + { + actual, err := q.VenueCountByCity(ctx) + if err != nil { + t.Error(err) + } + if diff := cmp.Diff(actual, []VenueCountByCityRow{ + {city.Slug, int64(1)}, + }); diff != "" { + t.Errorf("venue count mismatch:\n%s", diff) + } + } + + { + actual, err := q.ListCities(ctx) + if err != nil { + t.Error(err) + } + if diff := cmp.Diff(actual, []City{city}); diff != "" { + t.Errorf("list city mismatch:\n%s", diff) + } + } + + { + actual, err := q.ListVenues(ctx, city.Slug) + if err != nil { + t.Error(err) + } + if diff := cmp.Diff(actual, []Venue{venue}); diff != "" { + t.Errorf("list venue mismatch:\n%s", diff) + } + } + + { + err := q.UpdateCityName(ctx, UpdateCityNameParams{ + Slug: city.Slug, + Name: "SF", + }) + if err != nil { + t.Error(err) + } + } + + { + expected := "Fillmore" + err := q.UpdateVenueName(ctx, UpdateVenueNameParams{ + Slug: venue.Slug, + Name: expected, + }) + if err != nil { + t.Error(err) + } + fresh, err := q.GetVenue(ctx, GetVenueParams{ + Slug: venue.Slug, + City: city.Slug, + }) + if diff := cmp.Diff(expected, fresh.Name); diff != "" { + t.Errorf("update venue mismatch:\n%s", diff) + } + } + + { + err := q.DeleteVenue(ctx, DeleteVenueParams{ + Slug: venue.Slug, + Slug_2: venue.Slug, + }) + if err != nil { + t.Error(err) + } + } +} + +func TestPrepared(t *testing.T) { + t.Parallel() + + sdb, cleanup := sqltest.MySQL(t, []string{"schema"}) + defer cleanup() + + q, err := Prepare(context.Background(), sdb) + if err != nil { + t.Fatal(err) + } + + runOnDeckQueries(t, q) +} + +func TestQueries(t *testing.T) { + t.Parallel() + + sdb, cleanup := sqltest.MySQL(t, []string{"schema"}) + defer cleanup() + + runOnDeckQueries(t, New(sdb)) +} diff --git a/examples/ondeck/mysql/models.go b/examples/ondeck/mysql/models.go new file mode 100644 index 0000000000..3d74b01cc3 --- /dev/null +++ b/examples/ondeck/mysql/models.go @@ -0,0 +1,49 @@ +// Code generated by sqlc. DO NOT EDIT. + +package ondeck + +import ( + "database/sql" + "fmt" + "time" +) + +type Status string + +const ( + StatusOpen Status = "open" + StatusClosed Status = "closed" +) + +func (e *Status) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = Status(s) + case string: + *e = Status(s) + default: + return fmt.Errorf("unsupported scan type for Status: %T", src) + } + return nil +} + +type City struct { + Slug string `json:"slug"` + Name string `json:"name"` +} + +// Venues are places where muisc happens +type Venue struct { + ID int64 `json:"id"` + // Venues can be either open or closed + Status Status `json:"status"` + Statuses sql.NullString `json:"statuses"` + // This value appears in public URLs + Slug string `json:"slug"` + Name string `json:"name"` + City string `json:"city"` + SpotifyPlaylist string `json:"spotify_playlist"` + SongkickID sql.NullString `json:"songkick_id"` + Tags sql.NullString `json:"tags"` + CreatedAt time.Time `json:"created_at"` +} diff --git a/examples/ondeck/mysql/querier.go b/examples/ondeck/mysql/querier.go new file mode 100644 index 0000000000..60e8f0daa0 --- /dev/null +++ b/examples/ondeck/mysql/querier.go @@ -0,0 +1,23 @@ +// Code generated by sqlc. DO NOT EDIT. + +package ondeck + +import ( + "context" + "database/sql" +) + +type Querier interface { + CreateCity(ctx context.Context, arg CreateCityParams) (sql.Result, error) + CreateVenue(ctx context.Context, arg CreateVenueParams) (sql.Result, error) + DeleteVenue(ctx context.Context, arg DeleteVenueParams) error + GetCity(ctx context.Context, slug string) (City, error) + GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error) + ListCities(ctx context.Context) ([]City, error) + ListVenues(ctx context.Context, city string) ([]Venue, error) + UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error + UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) error + VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error) +} + +var _ Querier = (*Queries)(nil) diff --git a/examples/ondeck/mysql/query/city.sql b/examples/ondeck/mysql/query/city.sql new file mode 100644 index 0000000000..275f4f2e68 --- /dev/null +++ b/examples/ondeck/mysql/query/city.sql @@ -0,0 +1,23 @@ +/* name: ListCities :many */ +SELECT * +FROM city +ORDER BY name; + +/* name: GetCity :one */ +SELECT * +FROM city +WHERE slug = ?; + +/* name: CreateCity :execresult */ +INSERT INTO city ( + name, + slug +) VALUES ( + ?, + ? +); + +/* name: UpdateCityName :exec */ +UPDATE city +SET name = ? +WHERE slug = ?; diff --git a/examples/ondeck/mysql/query/venue.sql b/examples/ondeck/mysql/query/venue.sql new file mode 100644 index 0000000000..a1dd7a1633 --- /dev/null +++ b/examples/ondeck/mysql/query/venue.sql @@ -0,0 +1,48 @@ +/* name: ListVenues :many */ +SELECT * +FROM venue +WHERE city = ? +ORDER BY name; + +/* name: DeleteVenue :exec */ +DELETE FROM venue +WHERE slug = ? AND slug = ?; + +/* name: GetVenue :one */ +SELECT * +FROM venue +WHERE slug = ? AND city = ?; + +/* name: CreateVenue :execresult */ +INSERT INTO venue ( + slug, + name, + city, + created_at, + spotify_playlist, + status, + statuses, + tags +) VALUES ( + ?, + ?, + ?, + NOW(), + ?, + ?, + ?, + ? +); + +/* name: UpdateVenueName :exec */ +UPDATE venue +SET name = ? +WHERE slug = ?; + +/* name: VenueCountByCity :many */ +SELECT + city, + count(*) +FROM venue +GROUP BY 1 +ORDER BY 1; diff --git a/examples/ondeck/mysql/schema/0001_city.sql b/examples/ondeck/mysql/schema/0001_city.sql new file mode 100644 index 0000000000..6be35d16bf --- /dev/null +++ b/examples/ondeck/mysql/schema/0001_city.sql @@ -0,0 +1,4 @@ +CREATE TABLE city ( + slug varchar(255) PRIMARY KEY, + name text NOT NULL +) diff --git a/examples/ondeck/mysql/schema/0002_venue.sql b/examples/ondeck/mysql/schema/0002_venue.sql new file mode 100644 index 0000000000..4fc842cee0 --- /dev/null +++ b/examples/ondeck/mysql/schema/0002_venue.sql @@ -0,0 +1,12 @@ +CREATE TABLE venues ( + id SERIAL primary key, + dropped text, + status ENUM('open', 'closed') not null COMMENT 'Venues can be either open or closed', + statuses text, -- status[], + slug text not null COMMENT 'This value appears in public URLs', + name varchar(255) not null, + city text not null references city(slug), + spotify_playlist varchar(255) not null, + songkick_id text, + tags text -- text[] +) COMMENT='Venues are places where muisc happens'; diff --git a/examples/ondeck/schema/0003_add_column.sql b/examples/ondeck/mysql/schema/0003_add_column.sql similarity index 100% rename from examples/ondeck/schema/0003_add_column.sql rename to examples/ondeck/mysql/schema/0003_add_column.sql diff --git a/examples/ondeck/mysql/venue.sql.go b/examples/ondeck/mysql/venue.sql.go new file mode 100644 index 0000000000..30749fabf5 --- /dev/null +++ b/examples/ondeck/mysql/venue.sql.go @@ -0,0 +1,191 @@ +// Code generated by sqlc. DO NOT EDIT. +// source: venue.sql + +package ondeck + +import ( + "context" + "database/sql" +) + +const createVenue = `-- name: CreateVenue :execresult +INSERT INTO venue ( + slug, + name, + city, + created_at, + spotify_playlist, + status, + statuses, + tags +) VALUES ( + ?, + ?, + ?, + NOW(), + ?, + ?, + ?, + ? +) +` + +type CreateVenueParams struct { + Slug string `json:"slug"` + Name string `json:"name"` + City string `json:"city"` + SpotifyPlaylist string `json:"spotify_playlist"` + Status Status `json:"status"` + Statuses sql.NullString `json:"statuses"` + Tags sql.NullString `json:"tags"` +} + +func (q *Queries) CreateVenue(ctx context.Context, arg CreateVenueParams) (sql.Result, error) { + return q.exec(ctx, q.createVenueStmt, createVenue, + arg.Slug, + arg.Name, + arg.City, + arg.SpotifyPlaylist, + arg.Status, + arg.Statuses, + arg.Tags, + ) +} + +const deleteVenue = `-- name: DeleteVenue :exec +DELETE FROM venue +WHERE slug = ? AND slug = ? +` + +type DeleteVenueParams struct { + Slug string `json:"slug"` + Slug_2 string `json:"slug_2"` +} + +func (q *Queries) DeleteVenue(ctx context.Context, arg DeleteVenueParams) error { + _, err := q.exec(ctx, q.deleteVenueStmt, deleteVenue, arg.Slug, arg.Slug_2) + return err +} + +const getVenue = `-- name: GetVenue :one +SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at +FROM venue +WHERE slug = ? AND city = ? +` + +type GetVenueParams struct { + Slug string `json:"slug"` + City string `json:"city"` +} + +func (q *Queries) GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error) { + row := q.queryRow(ctx, q.getVenueStmt, getVenue, arg.Slug, arg.City) + var i Venue + err := row.Scan( + &i.ID, + &i.Status, + &i.Statuses, + &i.Slug, + &i.Name, + &i.City, + &i.SpotifyPlaylist, + &i.SongkickID, + &i.Tags, + &i.CreatedAt, + ) + return i, err +} + +const listVenues = `-- name: ListVenues :many +SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at +FROM venue +WHERE city = ? +ORDER BY name +` + +func (q *Queries) ListVenues(ctx context.Context, city string) ([]Venue, error) { + rows, err := q.query(ctx, q.listVenuesStmt, listVenues, city) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Venue + for rows.Next() { + var i Venue + if err := rows.Scan( + &i.ID, + &i.Status, + &i.Statuses, + &i.Slug, + &i.Name, + &i.City, + &i.SpotifyPlaylist, + &i.SongkickID, + &i.Tags, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateVenueName = `-- name: UpdateVenueName :exec +UPDATE venue +SET name = ? +WHERE slug = ? +` + +type UpdateVenueNameParams struct { + Name string `json:"name"` + Slug string `json:"slug"` +} + +func (q *Queries) UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) error { + _, err := q.exec(ctx, q.updateVenueNameStmt, updateVenueName, arg.Name, arg.Slug) + return err +} + +const venueCountByCity = `-- name: VenueCountByCity :many +SELECT + city, + count(*) +FROM venue +GROUP BY 1 +ORDER BY 1 +` + +type VenueCountByCityRow struct { + City string `json:"city"` + Count int64 `json:"count"` +} + +func (q *Queries) VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error) { + rows, err := q.query(ctx, q.venueCountByCityStmt, venueCountByCity) + if err != nil { + return nil, err + } + defer rows.Close() + var items []VenueCountByCityRow + for rows.Next() { + var i VenueCountByCityRow + if err := rows.Scan(&i.City, &i.Count); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/examples/ondeck/city.sql.go b/examples/ondeck/postgresql/city.sql.go similarity index 100% rename from examples/ondeck/city.sql.go rename to examples/ondeck/postgresql/city.sql.go diff --git a/examples/ondeck/postgresql/db.go b/examples/ondeck/postgresql/db.go new file mode 100644 index 0000000000..834ca6c41a --- /dev/null +++ b/examples/ondeck/postgresql/db.go @@ -0,0 +1,176 @@ +// Code generated by sqlc. DO NOT EDIT. + +package ondeck + +import ( + "context" + "database/sql" + "fmt" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +func Prepare(ctx context.Context, db DBTX) (*Queries, error) { + q := Queries{db: db} + var err error + if q.createCityStmt, err = db.PrepareContext(ctx, createCity); err != nil { + return nil, fmt.Errorf("error preparing query CreateCity: %w", err) + } + if q.createVenueStmt, err = db.PrepareContext(ctx, createVenue); err != nil { + return nil, fmt.Errorf("error preparing query CreateVenue: %w", err) + } + if q.deleteVenueStmt, err = db.PrepareContext(ctx, deleteVenue); err != nil { + return nil, fmt.Errorf("error preparing query DeleteVenue: %w", err) + } + if q.getCityStmt, err = db.PrepareContext(ctx, getCity); err != nil { + return nil, fmt.Errorf("error preparing query GetCity: %w", err) + } + if q.getVenueStmt, err = db.PrepareContext(ctx, getVenue); err != nil { + return nil, fmt.Errorf("error preparing query GetVenue: %w", err) + } + if q.listCitiesStmt, err = db.PrepareContext(ctx, listCities); err != nil { + return nil, fmt.Errorf("error preparing query ListCities: %w", err) + } + if q.listVenuesStmt, err = db.PrepareContext(ctx, listVenues); err != nil { + return nil, fmt.Errorf("error preparing query ListVenues: %w", err) + } + if q.updateCityNameStmt, err = db.PrepareContext(ctx, updateCityName); err != nil { + return nil, fmt.Errorf("error preparing query UpdateCityName: %w", err) + } + if q.updateVenueNameStmt, err = db.PrepareContext(ctx, updateVenueName); err != nil { + return nil, fmt.Errorf("error preparing query UpdateVenueName: %w", err) + } + if q.venueCountByCityStmt, err = db.PrepareContext(ctx, venueCountByCity); err != nil { + return nil, fmt.Errorf("error preparing query VenueCountByCity: %w", err) + } + return &q, nil +} + +func (q *Queries) Close() error { + var err error + if q.createCityStmt != nil { + if cerr := q.createCityStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing createCityStmt: %w", cerr) + } + } + if q.createVenueStmt != nil { + if cerr := q.createVenueStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing createVenueStmt: %w", cerr) + } + } + if q.deleteVenueStmt != nil { + if cerr := q.deleteVenueStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing deleteVenueStmt: %w", cerr) + } + } + if q.getCityStmt != nil { + if cerr := q.getCityStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing getCityStmt: %w", cerr) + } + } + if q.getVenueStmt != nil { + if cerr := q.getVenueStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing getVenueStmt: %w", cerr) + } + } + if q.listCitiesStmt != nil { + if cerr := q.listCitiesStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing listCitiesStmt: %w", cerr) + } + } + if q.listVenuesStmt != nil { + if cerr := q.listVenuesStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing listVenuesStmt: %w", cerr) + } + } + if q.updateCityNameStmt != nil { + if cerr := q.updateCityNameStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing updateCityNameStmt: %w", cerr) + } + } + if q.updateVenueNameStmt != nil { + if cerr := q.updateVenueNameStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing updateVenueNameStmt: %w", cerr) + } + } + if q.venueCountByCityStmt != nil { + if cerr := q.venueCountByCityStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing venueCountByCityStmt: %w", cerr) + } + } + return err +} + +func (q *Queries) exec(ctx context.Context, stmt *sql.Stmt, query string, args ...interface{}) (sql.Result, error) { + switch { + case stmt != nil && q.tx != nil: + return q.tx.StmtContext(ctx, stmt).ExecContext(ctx, args...) + case stmt != nil: + return stmt.ExecContext(ctx, args...) + default: + return q.db.ExecContext(ctx, query, args...) + } +} + +func (q *Queries) query(ctx context.Context, stmt *sql.Stmt, query string, args ...interface{}) (*sql.Rows, error) { + switch { + case stmt != nil && q.tx != nil: + return q.tx.StmtContext(ctx, stmt).QueryContext(ctx, args...) + case stmt != nil: + return stmt.QueryContext(ctx, args...) + default: + return q.db.QueryContext(ctx, query, args...) + } +} + +func (q *Queries) queryRow(ctx context.Context, stmt *sql.Stmt, query string, args ...interface{}) *sql.Row { + switch { + case stmt != nil && q.tx != nil: + return q.tx.StmtContext(ctx, stmt).QueryRowContext(ctx, args...) + case stmt != nil: + return stmt.QueryRowContext(ctx, args...) + default: + return q.db.QueryRowContext(ctx, query, args...) + } +} + +type Queries struct { + db DBTX + tx *sql.Tx + createCityStmt *sql.Stmt + createVenueStmt *sql.Stmt + deleteVenueStmt *sql.Stmt + getCityStmt *sql.Stmt + getVenueStmt *sql.Stmt + listCitiesStmt *sql.Stmt + listVenuesStmt *sql.Stmt + updateCityNameStmt *sql.Stmt + updateVenueNameStmt *sql.Stmt + venueCountByCityStmt *sql.Stmt +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + tx: tx, + createCityStmt: q.createCityStmt, + createVenueStmt: q.createVenueStmt, + deleteVenueStmt: q.deleteVenueStmt, + getCityStmt: q.getCityStmt, + getVenueStmt: q.getVenueStmt, + listCitiesStmt: q.listCitiesStmt, + listVenuesStmt: q.listVenuesStmt, + updateCityNameStmt: q.updateCityNameStmt, + updateVenueNameStmt: q.updateVenueNameStmt, + venueCountByCityStmt: q.venueCountByCityStmt, + } +} diff --git a/examples/ondeck/db_test.go b/examples/ondeck/postgresql/db_test.go similarity index 100% rename from examples/ondeck/db_test.go rename to examples/ondeck/postgresql/db_test.go diff --git a/examples/ondeck/models.go b/examples/ondeck/postgresql/models.go similarity index 100% rename from examples/ondeck/models.go rename to examples/ondeck/postgresql/models.go diff --git a/examples/ondeck/querier.go b/examples/ondeck/postgresql/querier.go similarity index 100% rename from examples/ondeck/querier.go rename to examples/ondeck/postgresql/querier.go diff --git a/examples/ondeck/query/city.sql b/examples/ondeck/postgresql/query/city.sql similarity index 100% rename from examples/ondeck/query/city.sql rename to examples/ondeck/postgresql/query/city.sql diff --git a/examples/ondeck/query/venue.sql b/examples/ondeck/postgresql/query/venue.sql similarity index 100% rename from examples/ondeck/query/venue.sql rename to examples/ondeck/postgresql/query/venue.sql diff --git a/examples/ondeck/schema/0001_city.sql b/examples/ondeck/postgresql/schema/0001_city.sql similarity index 100% rename from examples/ondeck/schema/0001_city.sql rename to examples/ondeck/postgresql/schema/0001_city.sql diff --git a/examples/ondeck/schema/0002_venue.sql b/examples/ondeck/postgresql/schema/0002_venue.sql similarity index 100% rename from examples/ondeck/schema/0002_venue.sql rename to examples/ondeck/postgresql/schema/0002_venue.sql diff --git a/examples/ondeck/postgresql/schema/0003_add_column.sql b/examples/ondeck/postgresql/schema/0003_add_column.sql new file mode 100644 index 0000000000..9b334bccce --- /dev/null +++ b/examples/ondeck/postgresql/schema/0003_add_column.sql @@ -0,0 +1,3 @@ +ALTER TABLE venues RENAME TO venue; +ALTER TABLE venue ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT NOW(); +ALTER TABLE venue DROP COLUMN dropped; diff --git a/examples/ondeck/venue.sql.go b/examples/ondeck/postgresql/venue.sql.go similarity index 100% rename from examples/ondeck/venue.sql.go rename to examples/ondeck/postgresql/venue.sql.go diff --git a/examples/ondeck/sqlc.json b/examples/ondeck/sqlc.json index 3b604f4c63..c6abb748d4 100644 --- a/examples/ondeck/sqlc.json +++ b/examples/ondeck/sqlc.json @@ -2,14 +2,24 @@ "version": "1", "packages": [ { - "path": ".", + "path": "postgresql", "name": "ondeck", - "schema": "schema", - "queries": "query", + "schema": "postgresql/schema", + "queries": "postgresql/query", "engine": "postgresql", "emit_json_tags": true, "emit_prepared_queries": true, "emit_interface": true + }, + { + "path": "mysql", + "name": "ondeck", + "schema": "mysql/schema", + "queries": "mysql/query", + "engine": "mysql:beta", + "emit_json_tags": true, + "emit_prepared_queries": true, + "emit_interface": true } ] } diff --git a/internal/compiler/engine.go b/internal/compiler/engine.go index 3d7d93d8e2..6d579caaa0 100644 --- a/internal/compiler/engine.go +++ b/internal/compiler/engine.go @@ -27,7 +27,7 @@ func NewCompiler(conf config.SQL, combo config.CombinedSettings) *Compiler { c.catalog = catalog.New("main") case config.EngineMySQL, config.EngineMySQLBeta: c.parser = dolphin.NewParser() - c.catalog = catalog.New("public") // TODO: What is the default database for MySQL? + c.catalog = dolphin.NewCatalog() case config.EnginePostgreSQL: c.parser = postgresql.NewParser() c.catalog = postgresql.NewCatalog() diff --git a/internal/engine/dolphin/catalog.go b/internal/engine/dolphin/catalog.go new file mode 100644 index 0000000000..4ce1c5f920 --- /dev/null +++ b/internal/engine/dolphin/catalog.go @@ -0,0 +1,35 @@ +package dolphin + +import ( + "github.com/kyleconroy/sqlc/internal/sql/ast" + "github.com/kyleconroy/sqlc/internal/sql/catalog" +) + +func NewCatalog() *catalog.Catalog { + def := "public" // TODO: What is the default database for MySQL? + return &catalog.Catalog{ + DefaultSchema: def, + Schemas: []*catalog.Schema{ + &catalog.Schema{ + Name: def, + Funcs: []*catalog.Function{ + { + Name: "count", + Args: []*catalog.Argument{ + { + Type: &ast.TypeName{Name: "any"}, + }, + }, + ReturnType: &ast.TypeName{Name: "bigint"}, + }, + { + Name: "count", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "bigint"}, + }, + }, + }, + }, + Extensions: map[string]struct{}{}, + } +} diff --git a/internal/engine/dolphin/convert.go b/internal/engine/dolphin/convert.go index 8f8e9c9677..6f56a2cba6 100644 --- a/internal/engine/dolphin/convert.go +++ b/internal/engine/dolphin/convert.go @@ -445,8 +445,34 @@ func (c *cc) convertAdminStmt(n *pcast.AdminStmt) ast.Node { return &ast.TODO{} } -func (c *cc) convertAggregateFuncExpr(n *pcast.AggregateFuncExpr) ast.Node { - return &ast.TODO{} +func (c *cc) convertAggregateFuncExpr(n *pcast.AggregateFuncExpr) *ast.FuncCall { + fn := &ast.FuncCall{ + Func: &ast.FuncName{ + Name: n.F, + }, + Funcname: &ast.List{ + Items: []ast.Node{ + &ast.String{ + Str: n.F, + }, + }, + }, + Args: &ast.List{}, + AggOrder: &ast.List{}, + } + for _, a := range n.Args { + if value, ok := a.(*driver.ValueExpr); ok { + if value.GetInt64() == int64(1) { + fn.AggStar = true + continue + } + } + fn.Args.Items = append(fn.Args.Items, c.convert(a)) + } + if n.Distinct { + fn.AggDistinct = true + } + return fn } func (c *cc) convertAlterDatabaseStmt(n *pcast.AlterDatabaseStmt) ast.Node { diff --git a/internal/engine/dolphin/utils.go b/internal/engine/dolphin/utils.go index 5783c6e5a6..6bbc315695 100644 --- a/internal/engine/dolphin/utils.go +++ b/internal/engine/dolphin/utils.go @@ -76,6 +76,9 @@ func isNotNull(n *pcast.ColumnDef) bool { if n.Options[i].Tp == pcast.ColumnOptionNotNull { return true } + if n.Options[i].Tp == pcast.ColumnOptionPrimaryKey { + return true + } } return false } diff --git a/internal/sqltest/mysql.go b/internal/sqltest/mysql.go index 781405ab70..d1cf92e6ad 100644 --- a/internal/sqltest/mysql.go +++ b/internal/sqltest/mysql.go @@ -50,6 +50,18 @@ func MySQL(t *testing.T, migrations []string) (*sql.DB, func()) { t.Fatal(err) } + // For each test, pick a new database name at random. + dbName := "sqltest_mysql_" + id() + if _, err := db.Exec("CREATE DATABASE " + dbName); err != nil { + t.Fatal(err) + } + + source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?multiStatements=true&parseTime=true", user, pass, host, port, dbName) + sdb, err := sql.Open("mysql", source) + if err != nil { + t.Fatal(err) + } + files, err := sqlpath.Glob(migrations) if err != nil { t.Fatal(err) @@ -59,10 +71,10 @@ func MySQL(t *testing.T, migrations []string) (*sql.DB, func()) { if err != nil { t.Fatal(err) } - if _, err := db.Exec(string(blob)); err != nil { + if _, err := sdb.Exec(string(blob)); err != nil { t.Fatalf("%s: %s", filepath.Base(f), err) } } - return db, func() {} + return sdb, func() {} } diff --git a/internal/sqltest/postgres.go b/internal/sqltest/postgres.go index bc5cb8dd2b..f85e933f43 100644 --- a/internal/sqltest/postgres.go +++ b/internal/sqltest/postgres.go @@ -66,10 +66,8 @@ func PostgreSQL(t *testing.T, migrations []string) (*sql.DB, func()) { t.Fatal(err) } - schema := "sqltest_" + id() - // For each test, pick a new schema name at random. - // `foo` is used here only as an example + schema := "sqltest_postgresql_" + id() if _, err := db.Exec("CREATE SCHEMA " + schema); err != nil { t.Fatal(err) }