From 95193d1fb44c42631f694ab90749174fa7536de4 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 6 Jun 2022 20:24:55 +0900 Subject: [PATCH 01/22] add test helper for SQLite --- go.mod | 1 + go.sum | 2 ++ internal/sqltest/sqlite.go | 46 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 internal/sqltest/sqlite.go diff --git a/go.mod b/go.mod index b1eadde071..5541606598 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/jackc/pgx/v4 v4.16.1 github.com/jinzhu/inflection v1.0.0 github.com/lib/pq v1.10.6 + github.com/mattn/go-sqlite3 v1.14.13 github.com/pganalyze/pg_query_go/v2 v2.1.0 github.com/pingcap/parser v0.0.0-20210914110036-002913dd28ec github.com/spf13/cobra v1.4.0 diff --git a/go.sum b/go.sum index 7c62716fcb..7404f39b62 100644 --- a/go.sum +++ b/go.sum @@ -119,6 +119,8 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I= +github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/pganalyze/pg_query_go/v2 v2.1.0 h1:donwPZ4G/X+kMs7j5eYtKjdziqyOLVp3pkUrzb9lDl8= github.com/pganalyze/pg_query_go/v2 v2.1.0/go.mod h1:XAxmVqz1tEGqizcQ3YSdN90vCOHBWjJi8URL1er5+cA= github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg= diff --git a/internal/sqltest/sqlite.go b/internal/sqltest/sqlite.go new file mode 100644 index 0000000000..6f8dd10a01 --- /dev/null +++ b/internal/sqltest/sqlite.go @@ -0,0 +1,46 @@ +package sqltest + +import ( + "database/sql" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/kyleconroy/sqlc/internal/sql/sqlpath" + + _ "github.com/mattn/go-sqlite3" +) + +func SQLite(t *testing.T, migrations []string) (*sql.DB, func()) { + t.Helper() + + // For each test, pick a new database name at random. + source, err := ioutil.TempFile("", "sqltest_sqlite_") + if err != nil { + t.Fatal(err) + } + t.Logf("open %s\n", source.Name()) + sdb, err := sql.Open("sqlite3", source.Name()) + if err != nil { + t.Fatal(err) + } + + 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) + } + if _, err := sdb.Exec(string(blob)); err != nil { + t.Fatalf("%s: %s", filepath.Base(f), err) + } + } + + return sdb, func() { + os.Remove(source.Name()) + } +} From eb8ac20fc2c99d9aad87e9e225e239d04a9c227e Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 6 Jun 2022 23:17:56 +0900 Subject: [PATCH 02/22] [sqlite] support simple insert and select where clause --- examples/authors/sqlc.json | 13 +++- examples/authors/sqlite/db.go | 31 ++++++++++ examples/authors/sqlite/db_test.go | 48 +++++++++++++++ examples/authors/sqlite/models.go | 15 +++++ examples/authors/sqlite/query.sql | 18 ++++++ examples/authors/sqlite/query.sql.go | 78 ++++++++++++++++++++++++ examples/authors/sqlite/schema.sql | 5 ++ internal/engine/sqlite/convert.go | 90 ++++++++++++++++++++++++++-- 8 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 examples/authors/sqlite/db.go create mode 100644 examples/authors/sqlite/db_test.go create mode 100644 examples/authors/sqlite/models.go create mode 100644 examples/authors/sqlite/query.sql create mode 100644 examples/authors/sqlite/query.sql.go create mode 100644 examples/authors/sqlite/schema.sql diff --git a/examples/authors/sqlc.json b/examples/authors/sqlc.json index 58feec2d4c..b482424b71 100644 --- a/examples/authors/sqlc.json +++ b/examples/authors/sqlc.json @@ -22,6 +22,17 @@ "out": "mysql" } } + }, + { + "schema": "sqlite/schema.sql", + "queries": "sqlite/query.sql", + "engine": "_lemon", + "gen": { + "go": { + "package": "authors", + "out": "sqlite" + } + } } ] -} +} \ No newline at end of file diff --git a/examples/authors/sqlite/db.go b/examples/authors/sqlite/db.go new file mode 100644 index 0000000000..da57c549a4 --- /dev/null +++ b/examples/authors/sqlite/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package authors + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/examples/authors/sqlite/db_test.go b/examples/authors/sqlite/db_test.go new file mode 100644 index 0000000000..db5326b79d --- /dev/null +++ b/examples/authors/sqlite/db_test.go @@ -0,0 +1,48 @@ +//go:build examples +// +build examples + +package authors + +import ( + "context" + "database/sql" + "testing" + + "github.com/kyleconroy/sqlc/internal/sqltest" +) + +func TestAuthors(t *testing.T) { + sdb, cleanup := sqltest.SQLite(t, []string{"schema.sql"}) + defer cleanup() + + ctx := context.Background() + db := New(sdb) + + // list all authors + authors, err := db.ListAuthors(ctx) + if err != nil { + t.Fatal(err) + } + t.Log(authors) + + // create an author + result, err := db.CreateAuthor(ctx, CreateAuthorParams{ + Name: "Brian Kernighan", + Bio: sql.NullString{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true}, + }) + if err != nil { + t.Fatal(err) + } + authorID, err := result.LastInsertId() + if err != nil { + t.Fatal(err) + } + t.Log(authorID) + + // get the author we just inserted + fetchedAuthor, err := db.GetAuthor(ctx, authorID) + if err != nil { + t.Fatal(err) + } + t.Log(fetchedAuthor) +} diff --git a/examples/authors/sqlite/models.go b/examples/authors/sqlite/models.go new file mode 100644 index 0000000000..514e8ffe60 --- /dev/null +++ b/examples/authors/sqlite/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package authors + +import ( + "database/sql" +) + +type Author struct { + ID int64 + Name string + Bio sql.NullString +} diff --git a/examples/authors/sqlite/query.sql b/examples/authors/sqlite/query.sql new file mode 100644 index 0000000000..c3b5866149 --- /dev/null +++ b/examples/authors/sqlite/query.sql @@ -0,0 +1,18 @@ +/* name: GetAuthor :one */ +SELECT * FROM authors +WHERE id = ? LIMIT 1; + +/* name: ListAuthors :many */ +SELECT * FROM authors +ORDER BY name; + +/* name: CreateAuthor :execresult */ +INSERT INTO authors ( + name, bio +) VALUES ( + ?, ? +); + +/* name: DeleteAuthor :exec */ +DELETE FROM authors +WHERE id = ?; diff --git a/examples/authors/sqlite/query.sql.go b/examples/authors/sqlite/query.sql.go new file mode 100644 index 0000000000..7776498157 --- /dev/null +++ b/examples/authors/sqlite/query.sql.go @@ -0,0 +1,78 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 +// source: query.sql + +package authors + +import ( + "context" + "database/sql" +) + +const createAuthor = `-- name: CreateAuthor :execresult +INSERT INTO authors ( + name, bio +) VALUES ( + ?, ? +) +` + +type CreateAuthorParams struct { + Name string + Bio sql.NullString +} + +func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (sql.Result, error) { + return q.db.ExecContext(ctx, createAuthor, arg.Name, arg.Bio) +} + +const deleteAuthor = `-- name: DeleteAuthor :exec +DELETE FROM authors +WHERE id = ? +` + +func (q *Queries) DeleteAuthor(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteAuthor, id) + return err +} + +const getAuthor = `-- name: GetAuthor :one +SELECT id, name, bio FROM authors +WHERE id = ? LIMIT 1 +` + +func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) { + row := q.db.QueryRowContext(ctx, getAuthor, id) + var i Author + err := row.Scan(&i.ID, &i.Name, &i.Bio) + return i, err +} + +const listAuthors = `-- name: ListAuthors :many +SELECT id, name, bio FROM authors +ORDER BY name +` + +func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) { + rows, err := q.db.QueryContext(ctx, listAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Author + for rows.Next() { + var i Author + if err := rows.Scan(&i.ID, &i.Name, &i.Bio); 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/authors/sqlite/schema.sql b/examples/authors/sqlite/schema.sql new file mode 100644 index 0000000000..9b81ece0e4 --- /dev/null +++ b/examples/authors/sqlite/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE authors ( + id integer PRIMARY KEY AUTOINCREMENT, + name text NOT NULL, + bio text +); diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index bde50cafac..69e500c401 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -1,10 +1,11 @@ package sqlite import ( - "github.com/antlr/antlr4/runtime/Go/antlr" "strconv" "strings" + "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/kyleconroy/sqlc/internal/engine/sqlite/parser" "github.com/kyleconroy/sqlc/internal/sql/ast" ) @@ -235,10 +236,18 @@ func convertSimpleSelect_stmtContext(c *parser.Simple_select_stmtContext) ast.No cols := getCols(core) tables := getTables(core) - return &ast.SelectStmt{ + stmt := &ast.SelectStmt{ FromClause: &ast.List{Items: tables}, TargetList: &ast.List{Items: cols}, } + + if core.WHERE_() != nil { + if core.Expr(0) != nil { + stmt.WhereClause = convert(core.Expr(0)) + } + } + + return stmt } return &ast.TODO{} @@ -247,6 +256,7 @@ func convertSimpleSelect_stmtContext(c *parser.Simple_select_stmtContext) ast.No func convertMultiSelect_stmtContext(c multiselect) ast.Node { var tables []ast.Node var cols []ast.Node + var where ast.Node for _, icore := range c.AllSelect_core() { core, ok := icore.(*parser.Select_coreContext) if !ok { @@ -254,10 +264,18 @@ func convertMultiSelect_stmtContext(c multiselect) ast.Node { } cols = append(cols, getCols(core)...) tables = append(tables, getTables(core)...) + + if core.WHERE_() != nil { + if core.Expr(0) != nil { + where = convert(core.Expr(0)) + } + } } + return &ast.SelectStmt{ - FromClause: &ast.List{Items: tables}, - TargetList: &ast.List{Items: cols}, + FromClause: &ast.List{Items: tables}, + TargetList: &ast.List{Items: cols}, + WhereClause: where, } } @@ -463,6 +481,67 @@ func convertParam(c *parser.Expr_bindContext) ast.Node { return &ast.TODO{} } +func convertInsert_stmtContext(c *parser.Insert_stmtContext) ast.Node { + if c == nil { + return nil + } + values := c.AllExpr() + if len(values) == 0 { + // TODO: add INSERT WITH SELECT support + return &ast.TODO{} + } + tableName := c.Table_name().GetText() + // we MUST have the columns in the update, + // otherwise the parser does not give ANY context, + // on "expression groups" + columns := convertCols(c.AllColumn_name()) + insertStmt := &ast.InsertStmt{ + Relation: &ast.RangeVar{ + Relname: &tableName, + }, + Cols: columns, + ReturningList: &ast.List{}, + SelectStmt: convertSelectStmt(values, len(c.AllColumn_name())), + } + return insertStmt +} + +func convertSelectStmt(values []parser.IExprContext, columns int) ast.Node { + valueList := &ast.List{Items: []ast.Node{}} + // the sqlite parse will give us values in a single slice + // so INSERT INTO a (b, c) VALUES (?, ?), (?, ?); + // will produce a 4 element slice. sqlite forces + // each column to have an expression so + // INSERT INTO a (b, c) VALUES (?); is invalid + // even if c is nullable + if columns == 0 { + columns = len(values) + } + for i := 0; i < len(values); i++ { + inner := &ast.List{Items: []ast.Node{}} + for ; i < columns; i++ { + inner.Items = append(inner.Items, convert(values[i])) + } + valueList.Items = append(valueList.Items, inner) + } + return &ast.SelectStmt{ + FromClause: &ast.List{}, + TargetList: &ast.List{}, + ValuesLists: valueList, + } +} + +func convertCols(columns []parser.IColumn_nameContext) *ast.List { + out := &ast.List{} + for _, c := range columns { + colName := c.GetText() + out.Items = append(out.Items, &ast.ResTarget{ + Name: &colName, + }) + } + return out +} + func convert(node node) ast.Node { switch n := node.(type) { @@ -521,6 +600,9 @@ func convert(node node) ast.Node { case *parser.Compound_select_stmtContext: return convertMultiSelect_stmtContext(n) + case *parser.Insert_stmtContext: + return convertInsert_stmtContext(n) + default: return &ast.TODO{} } From d9b4f40dc8d7e45a76278dcfd20f6d7982167a3a Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Sun, 12 Jun 2022 00:17:44 +0900 Subject: [PATCH 03/22] [sqlite] Improve simple select syntax support * Add support `where` and `join` clause * Add support table alias --- internal/engine/sqlite/convert.go | 82 ++++++++++++++----------------- 1 file changed, 37 insertions(+), 45 deletions(-) diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 69e500c401..0a63080e77 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -231,28 +231,6 @@ func convertComparison(c *parser.Expr_comparisonContext) ast.Node { return aExpr } -func convertSimpleSelect_stmtContext(c *parser.Simple_select_stmtContext) ast.Node { - if core, ok := c.Select_core().(*parser.Select_coreContext); ok { - cols := getCols(core) - tables := getTables(core) - - stmt := &ast.SelectStmt{ - FromClause: &ast.List{Items: tables}, - TargetList: &ast.List{Items: cols}, - } - - if core.WHERE_() != nil { - if core.Expr(0) != nil { - stmt.WhereClause = convert(core.Expr(0)) - } - } - - return stmt - } - - return &ast.TODO{} -} - func convertMultiSelect_stmtContext(c multiselect) ast.Node { var tables []ast.Node var cols []ast.Node @@ -281,22 +259,15 @@ func convertMultiSelect_stmtContext(c multiselect) ast.Node { func getTables(core *parser.Select_coreContext) []ast.Node { var tables []ast.Node - for _, ifrom := range core.AllTable_or_subquery() { - from, ok := ifrom.(*parser.Table_or_subqueryContext) - if !ok { - continue - } - rel := from.Table_name().GetText() - name := ast.RangeVar{ - Relname: &rel, - Location: from.GetStart().GetStart(), - } - if from.Schema_name() != nil { - text := from.Schema_name().GetText() - name.Schemaname = &text + tables = append(tables, convertTablesOrSubquery(core.AllTable_or_subquery())...) + + if core.Join_clause() != nil { + join, ok := core.Join_clause().(*parser.Join_clauseContext) + if ok { + tables = append(tables, convertTablesOrSubquery(join.AllTable_or_subquery())...) } - tables = append(tables, &name) } + return tables } @@ -542,6 +513,33 @@ func convertCols(columns []parser.IColumn_nameContext) *ast.List { return out } +func convertTablesOrSubquery(tableOrSubquery []parser.ITable_or_subqueryContext) []ast.Node { + var tables []ast.Node + for _, ifrom := range tableOrSubquery { + from, ok := ifrom.(*parser.Table_or_subqueryContext) + if !ok { + continue + } + rel := from.Table_name().GetText() + name := ast.RangeVar{ + Relname: &rel, + Location: from.GetStart().GetStart(), + } + if from.Schema_name() != nil { + schema := from.Schema_name().GetText() + name.Schemaname = &schema + } + if from.Table_alias() != nil { + alias := from.Table_alias().GetText() + name.Alias = &ast.Alias{Aliasname: &alias} + } + + tables = append(tables, &name) + } + + return tables +} + func convert(node node) ast.Node { switch n := node.(type) { @@ -588,21 +586,15 @@ func convert(node node) ast.Node { // TODO: need to handle this return &ast.TODO{} + case *parser.Insert_stmtContext: + return convertInsert_stmtContext(n) + case *parser.Select_stmtContext: return convertMultiSelect_stmtContext(n) case *parser.Sql_stmtContext: return convertSql_stmtContext(n) - case *parser.Simple_select_stmtContext: - return convertSimpleSelect_stmtContext(n) - - case *parser.Compound_select_stmtContext: - return convertMultiSelect_stmtContext(n) - - case *parser.Insert_stmtContext: - return convertInsert_stmtContext(n) - default: return &ast.TODO{} } From c2d7711d426e80ae6a6efe851069ce679d556f68 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Sun, 12 Jun 2022 01:29:09 +0900 Subject: [PATCH 04/22] [sqlite] add support simple update statement --- internal/engine/sqlite/convert.go | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 0a63080e77..09c3e30027 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -540,6 +540,44 @@ func convertTablesOrSubquery(tableOrSubquery []parser.ITable_or_subqueryContext) return tables } +func convertUpdate_stmtContext(c *parser.Update_stmtContext) ast.Node { + if c == nil { + return nil + } + + relations := &ast.List{} + tableName := c.Qualified_table_name().GetText() + rel := ast.RangeVar{ + Relname: &tableName, + Location: c.GetStart().GetStart(), + } + relations.Items = append(relations.Items, &rel) + + list := &ast.List{} + for i, col := range c.AllColumn_name() { + colName := col.GetText() + target := &ast.ResTarget{ + Name: &colName, + Val: convert(c.Expr(i)), + } + list.Items = append(list.Items, target) + } + + var where ast.Node = nil + if c.WHERE_() != nil { + where = convert(c.Expr(len(c.AllExpr()) - 1)) + } + + return &ast.UpdateStmt{ + Relations: relations, + TargetList: list, + WhereClause: where, + ReturningList: &ast.List{}, + FromClause: &ast.List{}, + WithClause: nil, // TODO: support with clause + } +} + func convert(node node) ast.Node { switch n := node.(type) { @@ -595,6 +633,9 @@ func convert(node node) ast.Node { case *parser.Sql_stmtContext: return convertSql_stmtContext(n) + case *parser.Update_stmtContext: + return convertUpdate_stmtContext(n) + default: return &ast.TODO{} } From de0923599b2d16d7284df978b006ee3a2245d3b2 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Sun, 12 Jun 2022 01:29:39 +0900 Subject: [PATCH 05/22] add booktest example for SQLite --- examples/booktest/sqlc.json | 9 +- examples/booktest/sqlite/db.go | 31 ++++ examples/booktest/sqlite/db_test.go | 174 ++++++++++++++++++ examples/booktest/sqlite/models.go | 25 +++ examples/booktest/sqlite/query.sql | 62 +++++++ examples/booktest/sqlite/query.sql.go | 251 ++++++++++++++++++++++++++ examples/booktest/sqlite/schema.sql | 20 ++ 7 files changed, 571 insertions(+), 1 deletion(-) create mode 100644 examples/booktest/sqlite/db.go create mode 100644 examples/booktest/sqlite/db_test.go create mode 100644 examples/booktest/sqlite/models.go create mode 100644 examples/booktest/sqlite/query.sql create mode 100644 examples/booktest/sqlite/query.sql.go create mode 100644 examples/booktest/sqlite/schema.sql diff --git a/examples/booktest/sqlc.json b/examples/booktest/sqlc.json index 419a759f19..4514428845 100644 --- a/examples/booktest/sqlc.json +++ b/examples/booktest/sqlc.json @@ -14,6 +14,13 @@ "schema": "mysql/schema.sql", "queries": "mysql/query.sql", "engine": "mysql" + }, + { + "name": "booktest", + "path": "sqlite", + "schema": "sqlite/schema.sql", + "queries": "sqlite/query.sql", + "engine": "_lemon" } ] -} +} \ No newline at end of file diff --git a/examples/booktest/sqlite/db.go b/examples/booktest/sqlite/db.go new file mode 100644 index 0000000000..9968f74d9e --- /dev/null +++ b/examples/booktest/sqlite/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package booktest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/examples/booktest/sqlite/db_test.go b/examples/booktest/sqlite/db_test.go new file mode 100644 index 0000000000..d00c719fef --- /dev/null +++ b/examples/booktest/sqlite/db_test.go @@ -0,0 +1,174 @@ +//go:build examples +// +build examples + +package booktest + +import ( + "context" + "testing" + "time" + + "github.com/kyleconroy/sqlc/internal/sqltest" +) + +// TODO: Enum is not yet supported +const ( + BookTypeFICTION string = "FICTION" + BookTypeNONFICTION string = "NONFICTION" +) + +func TestBooks(t *testing.T) { + db, cleanup := sqltest.SQLite(t, []string{"schema.sql"}) + defer cleanup() + + ctx := context.Background() + dq := New(db) + + // create an author + result, err := dq.CreateAuthor(ctx, "Unknown Master") + if err != nil { + t.Fatal(err) + } + authorID, err := result.LastInsertId() + if err != nil { + t.Fatal(err) + } + + // create transaction + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + + tq := dq.WithTx(tx) + + // save first book + now := time.Now() + _, err = tq.CreateBook(ctx, CreateBookParams{ + AuthorID: int64(authorID), + Isbn: "1", + Title: "my book title", + BookType: BookTypeFICTION, + Yr: 2016, + Available: now, + }) + if err != nil { + t.Fatal(err) + } + + // save second book + result, err = tq.CreateBook(ctx, CreateBookParams{ + AuthorID: int64(authorID), + Isbn: "2", + Title: "the second book", + BookType: BookTypeFICTION, + Yr: 2016, + Available: now, + Tags: "cool,unique", + }) + if err != nil { + t.Fatal(err) + } + bookOneID, err := result.LastInsertId() + if err != nil { + t.Fatal(err) + } + + // update the title and tags + err = tq.UpdateBook(ctx, UpdateBookParams{ + BookID: int64(bookOneID), + Title: "changed second title", + Tags: "cool,disastor", + }) + if err != nil { + t.Fatal(err) + } + + // save third book + _, err = tq.CreateBook(ctx, CreateBookParams{ + AuthorID: int64(authorID), + Isbn: "3", + Title: "the third book", + BookType: BookTypeFICTION, + Yr: 2001, + Available: now, + Tags: "cool", + }) + if err != nil { + t.Fatal(err) + } + + // save fourth book + result, err = tq.CreateBook(ctx, CreateBookParams{ + AuthorID: int64(authorID), + Isbn: "4", + Title: "4th place finisher", + BookType: BookTypeNONFICTION, + Yr: 2011, + Available: now, + Tags: "other", + }) + if err != nil { + t.Fatal(err) + } + bookThreeID, err := result.LastInsertId() + if err != nil { + t.Fatal(err) + } + + // tx commit + err = tx.Commit() + if err != nil { + t.Fatal(err) + } + + // upsert, changing ISBN and title + err = dq.UpdateBookISBN(ctx, UpdateBookISBNParams{ + BookID: int64(bookThreeID), + Isbn: "NEW ISBN", + Title: "never ever gonna finish, a quatrain", + Tags: "someother", + }) + if err != nil { + t.Fatal(err) + } + + // retrieve first book + books0, err := dq.BooksByTitleYear(ctx, BooksByTitleYearParams{ + Title: "my book title", + Yr: 2016, + }) + if err != nil { + t.Fatal(err) + } + for _, book := range books0 { + t.Logf("Book %d (%s): %s available: %s\n", book.BookID, book.BookType, book.Title, book.Available.Format(time.RFC822Z)) + author, err := dq.GetAuthor(ctx, book.AuthorID) + if err != nil { + t.Fatal(err) + } + t.Logf("Book %d author: %s\n", book.BookID, author.Name) + } + + // find a book with either "cool" or "other" tag + t.Logf("---------\nTag search results:\n") + res, err := dq.BooksByTags(ctx, "cool") + if err != nil { + t.Fatal(err) + } + for _, ab := range res { + t.Logf("Book %d: '%s', Author: '%s', ISBN: '%s' Tags: '%v'\n", ab.BookID, ab.Title, ab.Name, ab.Isbn, ab.Tags) + } + + // TODO: call say_hello(varchar) + + // get book 4 and delete + b5, err := dq.GetBook(ctx, int64(bookThreeID)) + if err != nil { + t.Fatal(err) + } + if err := dq.DeleteBook(ctx, b5.BookID); err != nil { + t.Fatal(err) + } + +} diff --git a/examples/booktest/sqlite/models.go b/examples/booktest/sqlite/models.go new file mode 100644 index 0000000000..d296fe2aee --- /dev/null +++ b/examples/booktest/sqlite/models.go @@ -0,0 +1,25 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package booktest + +import ( + "time" +) + +type Author struct { + AuthorID int64 + Name string +} + +type Book struct { + BookID int64 + AuthorID int64 + Isbn string + BookType string + Title string + Yr int64 + Available time.Time + Tags string +} diff --git a/examples/booktest/sqlite/query.sql b/examples/booktest/sqlite/query.sql new file mode 100644 index 0000000000..dd4f304ffd --- /dev/null +++ b/examples/booktest/sqlite/query.sql @@ -0,0 +1,62 @@ +/* name: GetAuthor :one */ +SELECT * FROM authors +WHERE author_id = ?; + +/* name: GetBook :one */ +SELECT * FROM books +WHERE book_id = ?; + +/* name: DeleteBook :exec */ +DELETE FROM books +WHERE book_id = ?; + +/* name: BooksByTitleYear :many */ +SELECT * FROM books +WHERE title = ? AND yr = ?; + +/* name: BooksByTags :many */ +SELECT + book_id, + title, + name, + isbn, + tags +FROM books +LEFT JOIN authors ON books.author_id = authors.author_id +WHERE tags = ?; + +/* name: CreateAuthor :execresult */ +INSERT INTO authors (name) VALUES (?); + +/* name: CreateBook :execresult */ +INSERT INTO books ( + author_id, + isbn, + book_type, + title, + yr, + available, + tags +) VALUES ( + ?, + ?, + ?, + ?, + ?, + ?, + ? +); + +/* name: UpdateBook :exec */ +UPDATE books +SET title = ?, tags = ? +WHERE book_id = ?; + +/* name: UpdateBookISBN :exec */ +UPDATE books +SET title = ?, tags = ?, isbn = ? +WHERE book_id = ?; + +/* name: DeleteAuthorBeforeYear :exec */ +DELETE FROM books +WHERE yr < ? AND author_id = ?; diff --git a/examples/booktest/sqlite/query.sql.go b/examples/booktest/sqlite/query.sql.go new file mode 100644 index 0000000000..32c0999468 --- /dev/null +++ b/examples/booktest/sqlite/query.sql.go @@ -0,0 +1,251 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 +// source: query.sql + +package booktest + +import ( + "context" + "database/sql" + "time" +) + +const booksByTags = `-- name: BooksByTags :many +SELECT + book_id, + title, + name, + isbn, + tags +FROM books +LEFT JOIN authors ON books.author_id = authors.author_id +WHERE tags = ? +` + +type BooksByTagsRow struct { + BookID int64 + Title string + Name string + Isbn string + Tags string +} + +func (q *Queries) BooksByTags(ctx context.Context, tags string) ([]BooksByTagsRow, error) { + rows, err := q.db.QueryContext(ctx, booksByTags, tags) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BooksByTagsRow + for rows.Next() { + var i BooksByTagsRow + if err := rows.Scan( + &i.BookID, + &i.Title, + &i.Name, + &i.Isbn, + &i.Tags, + ); 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 booksByTitleYear = `-- name: BooksByTitleYear :many +SELECT book_id, author_id, isbn, book_type, title, yr, available, tags FROM books +WHERE title = ? AND yr = ? +` + +type BooksByTitleYearParams struct { + Title string + Yr int64 +} + +func (q *Queries) BooksByTitleYear(ctx context.Context, arg BooksByTitleYearParams) ([]Book, error) { + rows, err := q.db.QueryContext(ctx, booksByTitleYear, arg.Title, arg.Yr) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Book + for rows.Next() { + var i Book + if err := rows.Scan( + &i.BookID, + &i.AuthorID, + &i.Isbn, + &i.BookType, + &i.Title, + &i.Yr, + &i.Available, + &i.Tags, + ); 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 createAuthor = `-- name: CreateAuthor :execresult +INSERT INTO authors (name) VALUES (?) +` + +func (q *Queries) CreateAuthor(ctx context.Context, name string) (sql.Result, error) { + return q.db.ExecContext(ctx, createAuthor, name) +} + +const createBook = `-- name: CreateBook :execresult +INSERT INTO books ( + author_id, + isbn, + book_type, + title, + yr, + available, + tags +) VALUES ( + ?, + ?, + ?, + ?, + ?, + ?, + ? +) +` + +type CreateBookParams struct { + AuthorID int64 + Isbn string + BookType string + Title string + Yr int64 + Available time.Time + Tags string +} + +func (q *Queries) CreateBook(ctx context.Context, arg CreateBookParams) (sql.Result, error) { + return q.db.ExecContext(ctx, createBook, + arg.AuthorID, + arg.Isbn, + arg.BookType, + arg.Title, + arg.Yr, + arg.Available, + arg.Tags, + ) +} + +const deleteAuthorBeforeYear = `-- name: DeleteAuthorBeforeYear :exec +DELETE FROM books +WHERE yr < ? AND author_id = ? +` + +type DeleteAuthorBeforeYearParams struct { + Yr int64 + AuthorID int64 +} + +func (q *Queries) DeleteAuthorBeforeYear(ctx context.Context, arg DeleteAuthorBeforeYearParams) error { + _, err := q.db.ExecContext(ctx, deleteAuthorBeforeYear, arg.Yr, arg.AuthorID) + return err +} + +const deleteBook = `-- name: DeleteBook :exec +DELETE FROM books +WHERE book_id = ? +` + +func (q *Queries) DeleteBook(ctx context.Context, bookID int64) error { + _, err := q.db.ExecContext(ctx, deleteBook, bookID) + return err +} + +const getAuthor = `-- name: GetAuthor :one +SELECT author_id, name FROM authors +WHERE author_id = ? +` + +func (q *Queries) GetAuthor(ctx context.Context, authorID int64) (Author, error) { + row := q.db.QueryRowContext(ctx, getAuthor, authorID) + var i Author + err := row.Scan(&i.AuthorID, &i.Name) + return i, err +} + +const getBook = `-- name: GetBook :one +SELECT book_id, author_id, isbn, book_type, title, yr, available, tags FROM books +WHERE book_id = ? +` + +func (q *Queries) GetBook(ctx context.Context, bookID int64) (Book, error) { + row := q.db.QueryRowContext(ctx, getBook, bookID) + var i Book + err := row.Scan( + &i.BookID, + &i.AuthorID, + &i.Isbn, + &i.BookType, + &i.Title, + &i.Yr, + &i.Available, + &i.Tags, + ) + return i, err +} + +const updateBook = `-- name: UpdateBook :exec +UPDATE books +SET title = ?, tags = ? +WHERE book_id = ? +` + +type UpdateBookParams struct { + Title string + Tags string + BookID int64 +} + +func (q *Queries) UpdateBook(ctx context.Context, arg UpdateBookParams) error { + _, err := q.db.ExecContext(ctx, updateBook, arg.Title, arg.Tags, arg.BookID) + return err +} + +const updateBookISBN = `-- name: UpdateBookISBN :exec +UPDATE books +SET title = ?, tags = ?, isbn = ? +WHERE book_id = ? +` + +type UpdateBookISBNParams struct { + Title string + Tags string + Isbn string + BookID int64 +} + +func (q *Queries) UpdateBookISBN(ctx context.Context, arg UpdateBookISBNParams) error { + _, err := q.db.ExecContext(ctx, updateBookISBN, + arg.Title, + arg.Tags, + arg.Isbn, + arg.BookID, + ) + return err +} diff --git a/examples/booktest/sqlite/schema.sql b/examples/booktest/sqlite/schema.sql new file mode 100644 index 0000000000..1176dcef28 --- /dev/null +++ b/examples/booktest/sqlite/schema.sql @@ -0,0 +1,20 @@ +CREATE TABLE authors ( + author_id integer NOT NULL PRIMARY KEY AUTOINCREMENT, + name text NOT NULL +); + +CREATE INDEX authors_name_idx ON authors(name); + +CREATE TABLE books ( + book_id integer NOT NULL PRIMARY KEY AUTOINCREMENT, + author_id integer NOT NULL, + isbn varchar(255) NOT NULL DEFAULT '' UNIQUE, + book_type text NOT NULL DEFAULT 'FICTION', + title text NOT NULL, + yr integer NOT NULL DEFAULT 2000, + available datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + tags text NOT NULL, + CHECK (book_type = 'FICTION' OR book_type = 'NONFICTION') +); + +CREATE INDEX books_title_idx ON books(title, yr); From a28eebd9c7d50595508f983400a4ffa9844b8498 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 00:26:32 +0900 Subject: [PATCH 06/22] [sqlite] support add column with NOT NULL constraint --- internal/engine/sqlite/convert.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 09c3e30027..c92a08ae76 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -52,6 +52,7 @@ func convertAlter_table_stmtContext(c *parser.Alter_table_stmtContext) ast.Node TypeName: &ast.TypeName{ Name: def.Type_name().GetText(), }, + IsNotNull: hasNotNullConstraint(def.AllColumn_constraint()), }, }) return stmt From e1a988684a3678a0dacfc891fd0c077c990417c7 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 00:30:26 +0900 Subject: [PATCH 07/22] add ondeck example for SQLite --- examples/booktest/sqlite/db_test.go | 12 +- examples/ondeck/sqlc.json | 12 +- examples/ondeck/sqlite/city.sql.go | 88 ++++++++ examples/ondeck/sqlite/db.go | 178 ++++++++++++++++ examples/ondeck/sqlite/db_test.go | 170 +++++++++++++++ examples/ondeck/sqlite/models.go | 28 +++ examples/ondeck/sqlite/querier.go | 25 +++ examples/ondeck/sqlite/query/city.sql | 23 +++ examples/ondeck/sqlite/query/venue.sql | 48 +++++ examples/ondeck/sqlite/schema/0001_city.sql | 4 + examples/ondeck/sqlite/schema/0002_venue.sql | 13 ++ .../ondeck/sqlite/schema/0003_add_column.sql | 3 + examples/ondeck/sqlite/venue.sql.go | 193 ++++++++++++++++++ 13 files changed, 790 insertions(+), 7 deletions(-) create mode 100644 examples/ondeck/sqlite/city.sql.go create mode 100644 examples/ondeck/sqlite/db.go create mode 100644 examples/ondeck/sqlite/db_test.go create mode 100644 examples/ondeck/sqlite/models.go create mode 100644 examples/ondeck/sqlite/querier.go create mode 100644 examples/ondeck/sqlite/query/city.sql create mode 100644 examples/ondeck/sqlite/query/venue.sql create mode 100644 examples/ondeck/sqlite/schema/0001_city.sql create mode 100644 examples/ondeck/sqlite/schema/0002_venue.sql create mode 100644 examples/ondeck/sqlite/schema/0003_add_column.sql create mode 100644 examples/ondeck/sqlite/venue.sql.go diff --git a/examples/booktest/sqlite/db_test.go b/examples/booktest/sqlite/db_test.go index d00c719fef..da4a4ac025 100644 --- a/examples/booktest/sqlite/db_test.go +++ b/examples/booktest/sqlite/db_test.go @@ -13,8 +13,8 @@ import ( // TODO: Enum is not yet supported const ( - BookTypeFICTION string = "FICTION" - BookTypeNONFICTION string = "NONFICTION" + BooksBookTypeFICTION string = "FICTION" + BooksBookTypeNONFICTION string = "NONFICTION" ) func TestBooks(t *testing.T) { @@ -48,7 +48,7 @@ func TestBooks(t *testing.T) { AuthorID: int64(authorID), Isbn: "1", Title: "my book title", - BookType: BookTypeFICTION, + BookType: BooksBookTypeFICTION, Yr: 2016, Available: now, }) @@ -61,7 +61,7 @@ func TestBooks(t *testing.T) { AuthorID: int64(authorID), Isbn: "2", Title: "the second book", - BookType: BookTypeFICTION, + BookType: BooksBookTypeFICTION, Yr: 2016, Available: now, Tags: "cool,unique", @@ -89,7 +89,7 @@ func TestBooks(t *testing.T) { AuthorID: int64(authorID), Isbn: "3", Title: "the third book", - BookType: BookTypeFICTION, + BookType: BooksBookTypeFICTION, Yr: 2001, Available: now, Tags: "cool", @@ -103,7 +103,7 @@ func TestBooks(t *testing.T) { AuthorID: int64(authorID), Isbn: "4", Title: "4th place finisher", - BookType: BookTypeNONFICTION, + BookType: BooksBookTypeFICTION, Yr: 2011, Available: now, Tags: "other", diff --git a/examples/ondeck/sqlc.json b/examples/ondeck/sqlc.json index 31f8e821b0..f45419823f 100644 --- a/examples/ondeck/sqlc.json +++ b/examples/ondeck/sqlc.json @@ -20,6 +20,16 @@ "emit_json_tags": true, "emit_prepared_queries": true, "emit_interface": true + }, + { + "path": "sqlite", + "name": "ondeck", + "schema": "sqlite/schema", + "queries": "sqlite/query", + "engine": "_lemon", + "emit_json_tags": true, + "emit_prepared_queries": true, + "emit_interface": true } ] -} +} \ No newline at end of file diff --git a/examples/ondeck/sqlite/city.sql.go b/examples/ondeck/sqlite/city.sql.go new file mode 100644 index 0000000000..4aed1d4ee3 --- /dev/null +++ b/examples/ondeck/sqlite/city.sql.go @@ -0,0 +1,88 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 +// source: city.sql + +package ondeck + +import ( + "context" +) + +const createCity = `-- name: CreateCity :exec +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) error { + _, err := q.exec(ctx, q.createCityStmt, createCity, arg.Name, arg.Slug) + return err +} + +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/sqlite/db.go b/examples/ondeck/sqlite/db.go new file mode 100644 index 0000000000..6c350452db --- /dev/null +++ b/examples/ondeck/sqlite/db.go @@ -0,0 +1,178 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +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/sqlite/db_test.go b/examples/ondeck/sqlite/db_test.go new file mode 100644 index 0000000000..4c73d13787 --- /dev/null +++ b/examples/ondeck/sqlite/db_test.go @@ -0,0 +1,170 @@ +//go:build examples +// +build examples + +package ondeck + +import ( + "context" + "database/sql" + "strings" + "testing" + + "github.com/kyleconroy/sqlc/internal/sqltest" + + "github.com/google/go-cmp/cmp" +) + +// TODO: Enum is not yet supported +const ( + VenuesStatusOpen string = "open" + VenuesStatusClosed string = "closed" +) + +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: VenuesStatusOpen, + Statuses: join(string(VenuesStatusOpen), string(VenuesStatusClosed)), + 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.SQLite(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.SQLite(t, []string{"schema"}) + defer cleanup() + + runOnDeckQueries(t, New(sdb)) +} diff --git a/examples/ondeck/sqlite/models.go b/examples/ondeck/sqlite/models.go new file mode 100644 index 0000000000..db5256b4d3 --- /dev/null +++ b/examples/ondeck/sqlite/models.go @@ -0,0 +1,28 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package ondeck + +import ( + "database/sql" + "time" +) + +type City struct { + Slug string `json:"slug"` + Name string `json:"name"` +} + +type Venue struct { + ID int64 `json:"id"` + Status string `json:"status"` + Statuses sql.NullString `json:"statuses"` + 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/sqlite/querier.go b/examples/ondeck/sqlite/querier.go new file mode 100644 index 0000000000..4814c42556 --- /dev/null +++ b/examples/ondeck/sqlite/querier.go @@ -0,0 +1,25 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package ondeck + +import ( + "context" + "database/sql" +) + +type Querier interface { + CreateCity(ctx context.Context, arg CreateCityParams) 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/sqlite/query/city.sql b/examples/ondeck/sqlite/query/city.sql new file mode 100644 index 0000000000..c387e9d000 --- /dev/null +++ b/examples/ondeck/sqlite/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 :exec */ +INSERT INTO city ( + name, + slug +) VALUES ( + ?, + ? +); + +/* name: UpdateCityName :exec */ +UPDATE city +SET name = ? +WHERE slug = ?; diff --git a/examples/ondeck/sqlite/query/venue.sql b/examples/ondeck/sqlite/query/venue.sql new file mode 100644 index 0000000000..b4f5fd4071 --- /dev/null +++ b/examples/ondeck/sqlite/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 ( + ?, + ?, + ?, + CURRENT_TIMESTAMP, + ?, + ?, + ?, + ? +); + +/* 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/sqlite/schema/0001_city.sql b/examples/ondeck/sqlite/schema/0001_city.sql new file mode 100644 index 0000000000..6be35d16bf --- /dev/null +++ b/examples/ondeck/sqlite/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/sqlite/schema/0002_venue.sql b/examples/ondeck/sqlite/schema/0002_venue.sql new file mode 100644 index 0000000000..e57166e4c0 --- /dev/null +++ b/examples/ondeck/sqlite/schema/0002_venue.sql @@ -0,0 +1,13 @@ +CREATE TABLE venues ( + id integer primary key AUTOINCREMENT, + dropped text, + status text not null, + statuses text, -- status[] + slug text not null, + name varchar(255) not null, + city text not null references city(slug), + spotify_playlist varchar(255) not null, + songkick_id text, + tags text, -- tags[] + CHECK (status = 'open' OR status = 'closed') +); diff --git a/examples/ondeck/sqlite/schema/0003_add_column.sql b/examples/ondeck/sqlite/schema/0003_add_column.sql new file mode 100644 index 0000000000..7d7a6443b4 --- /dev/null +++ b/examples/ondeck/sqlite/schema/0003_add_column.sql @@ -0,0 +1,3 @@ +ALTER TABLE venues RENAME TO venue; +ALTER TABLE venue ADD COLUMN created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; +ALTER TABLE venue DROP COLUMN dropped; diff --git a/examples/ondeck/sqlite/venue.sql.go b/examples/ondeck/sqlite/venue.sql.go new file mode 100644 index 0000000000..5028f1b507 --- /dev/null +++ b/examples/ondeck/sqlite/venue.sql.go @@ -0,0 +1,193 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 +// 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 ( + ?, + ?, + ?, + CURRENT_TIMESTAMP, + ?, + ?, + ?, + ? +) +` + +type CreateVenueParams struct { + Slug string `json:"slug"` + Name string `json:"name"` + City string `json:"city"` + SpotifyPlaylist string `json:"spotify_playlist"` + Status string `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 +} From 4b558d40c3c79fd92d4556a35b1b545480138e7a Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 19:55:02 +0900 Subject: [PATCH 08/22] regenerate example by sqlc 1.14 --- examples/authors/sqlite/db.go | 2 +- examples/authors/sqlite/models.go | 2 +- examples/authors/sqlite/query.sql.go | 2 +- examples/booktest/sqlite/db.go | 2 +- examples/booktest/sqlite/models.go | 2 +- examples/booktest/sqlite/query.sql.go | 2 +- examples/ondeck/sqlite/city.sql.go | 2 +- examples/ondeck/sqlite/db.go | 2 +- examples/ondeck/sqlite/models.go | 2 +- examples/ondeck/sqlite/querier.go | 2 +- examples/ondeck/sqlite/venue.sql.go | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/authors/sqlite/db.go b/examples/authors/sqlite/db.go index da57c549a4..c78f28adbd 100644 --- a/examples/authors/sqlite/db.go +++ b/examples/authors/sqlite/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 package authors diff --git a/examples/authors/sqlite/models.go b/examples/authors/sqlite/models.go index 514e8ffe60..f1aefb0be2 100644 --- a/examples/authors/sqlite/models.go +++ b/examples/authors/sqlite/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 package authors diff --git a/examples/authors/sqlite/query.sql.go b/examples/authors/sqlite/query.sql.go index 7776498157..6247b79ecb 100644 --- a/examples/authors/sqlite/query.sql.go +++ b/examples/authors/sqlite/query.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 // source: query.sql package authors diff --git a/examples/booktest/sqlite/db.go b/examples/booktest/sqlite/db.go index 9968f74d9e..89be65c809 100644 --- a/examples/booktest/sqlite/db.go +++ b/examples/booktest/sqlite/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 package booktest diff --git a/examples/booktest/sqlite/models.go b/examples/booktest/sqlite/models.go index d296fe2aee..c9b8e96bcc 100644 --- a/examples/booktest/sqlite/models.go +++ b/examples/booktest/sqlite/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 package booktest diff --git a/examples/booktest/sqlite/query.sql.go b/examples/booktest/sqlite/query.sql.go index 32c0999468..9a7b85f07b 100644 --- a/examples/booktest/sqlite/query.sql.go +++ b/examples/booktest/sqlite/query.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 // source: query.sql package booktest diff --git a/examples/ondeck/sqlite/city.sql.go b/examples/ondeck/sqlite/city.sql.go index 4aed1d4ee3..5fdeb516f9 100644 --- a/examples/ondeck/sqlite/city.sql.go +++ b/examples/ondeck/sqlite/city.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 // source: city.sql package ondeck diff --git a/examples/ondeck/sqlite/db.go b/examples/ondeck/sqlite/db.go index 6c350452db..4a40ff0fa4 100644 --- a/examples/ondeck/sqlite/db.go +++ b/examples/ondeck/sqlite/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 package ondeck diff --git a/examples/ondeck/sqlite/models.go b/examples/ondeck/sqlite/models.go index db5256b4d3..16804087d0 100644 --- a/examples/ondeck/sqlite/models.go +++ b/examples/ondeck/sqlite/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 package ondeck diff --git a/examples/ondeck/sqlite/querier.go b/examples/ondeck/sqlite/querier.go index 4814c42556..244b24cb17 100644 --- a/examples/ondeck/sqlite/querier.go +++ b/examples/ondeck/sqlite/querier.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 package ondeck diff --git a/examples/ondeck/sqlite/venue.sql.go b/examples/ondeck/sqlite/venue.sql.go index 5028f1b507..7ee4a9098c 100644 --- a/examples/ondeck/sqlite/venue.sql.go +++ b/examples/ondeck/sqlite/venue.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.13.0 +// sqlc v1.14.0 // source: venue.sql package ondeck From a9ee8b00bc3541a1e640c4ff0f78116c9b8d16e4 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 19:56:46 +0900 Subject: [PATCH 09/22] [sqlite] add endtoend test for alias --- .../endtoend/testdata/alias/sqlite/go/db.go | 31 +++++++++++++++++++ .../testdata/alias/sqlite/go/models.go | 11 +++++++ .../testdata/alias/sqlite/go/query.sql.go | 20 ++++++++++++ .../endtoend/testdata/alias/sqlite/query.sql | 5 +++ .../endtoend/testdata/alias/sqlite/sqlc.json | 12 +++++++ 5 files changed, 79 insertions(+) create mode 100644 internal/endtoend/testdata/alias/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/alias/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/alias/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/alias/sqlite/query.sql create mode 100644 internal/endtoend/testdata/alias/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/alias/sqlite/go/db.go b/internal/endtoend/testdata/alias/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/alias/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/alias/sqlite/go/models.go b/internal/endtoend/testdata/alias/sqlite/go/models.go new file mode 100644 index 0000000000..a3136ad600 --- /dev/null +++ b/internal/endtoend/testdata/alias/sqlite/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Bar struct { + ID int64 +} diff --git a/internal/endtoend/testdata/alias/sqlite/go/query.sql.go b/internal/endtoend/testdata/alias/sqlite/go/query.sql.go new file mode 100644 index 0000000000..d42e767bd5 --- /dev/null +++ b/internal/endtoend/testdata/alias/sqlite/go/query.sql.go @@ -0,0 +1,20 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const aliasBar = `-- name: AliasBar :exec +DELETE FROM bar AS b +WHERE b.id = ? +` + +func (q *Queries) AliasBar(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, aliasBar, id) + return err +} diff --git a/internal/endtoend/testdata/alias/sqlite/query.sql b/internal/endtoend/testdata/alias/sqlite/query.sql new file mode 100644 index 0000000000..aa69308cd2 --- /dev/null +++ b/internal/endtoend/testdata/alias/sqlite/query.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar (id integer NOT NULL PRIMARY KEY AUTOINCREMENT); + +-- name: AliasBar :exec +DELETE FROM bar AS b +WHERE b.id = ?; diff --git a/internal/endtoend/testdata/alias/sqlite/sqlc.json b/internal/endtoend/testdata/alias/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/alias/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file From 6de9932a0f1705bf9dce43161ec9d22b23664602 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 19:57:36 +0900 Subject: [PATCH 10/22] [sqlite] support coalesce function --- .../testdata/coalesce/sqlite/go/db.go | 31 ++++++++ .../testdata/coalesce/sqlite/go/models.go | 14 ++++ .../testdata/coalesce/sqlite/go/query.sql.go | 73 +++++++++++++++++++ .../testdata/coalesce/sqlite/query.sql | 9 +++ .../testdata/coalesce/sqlite/sqlc.json | 12 +++ .../testdata/coalesce_as/sqlite/go/db.go | 31 ++++++++ .../testdata/coalesce_as/sqlite/go/models.go | 14 ++++ .../coalesce_as/sqlite/go/query.sql.go | 45 ++++++++++++ .../testdata/coalesce_as/sqlite/query.sql | 9 +++ .../testdata/coalesce_as/sqlite/sqlc.json | 12 +++ internal/engine/sqlite/convert.go | 37 ++++++---- 11 files changed, 271 insertions(+), 16 deletions(-) create mode 100644 internal/endtoend/testdata/coalesce/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/coalesce/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/coalesce/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/coalesce/sqlite/query.sql create mode 100644 internal/endtoend/testdata/coalesce/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/coalesce_as/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/coalesce_as/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/coalesce_as/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/coalesce_as/sqlite/query.sql create mode 100644 internal/endtoend/testdata/coalesce_as/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/coalesce/sqlite/go/db.go b/internal/endtoend/testdata/coalesce/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/coalesce/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/coalesce/sqlite/go/models.go b/internal/endtoend/testdata/coalesce/sqlite/go/models.go new file mode 100644 index 0000000000..5379ec6980 --- /dev/null +++ b/internal/endtoend/testdata/coalesce/sqlite/go/models.go @@ -0,0 +1,14 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Foo struct { + Bar sql.NullString + Bat string +} diff --git a/internal/endtoend/testdata/coalesce/sqlite/go/query.sql.go b/internal/endtoend/testdata/coalesce/sqlite/go/query.sql.go new file mode 100644 index 0000000000..d32f3560de --- /dev/null +++ b/internal/endtoend/testdata/coalesce/sqlite/go/query.sql.go @@ -0,0 +1,73 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const coalesce = `-- name: Coalesce :many +SELECT coalesce(bar, '') as login +FROM foo +` + +func (q *Queries) Coalesce(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, coalesce) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var login string + if err := rows.Scan(&login); err != nil { + return nil, err + } + items = append(items, login) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const coalesceColumns = `-- name: CoalesceColumns :many +SELECT bar, bat, coalesce(bar, bat) +FROM foo +` + +type CoalesceColumnsRow struct { + Bar sql.NullString + Bat string + Bar_2 string +} + +func (q *Queries) CoalesceColumns(ctx context.Context) ([]CoalesceColumnsRow, error) { + rows, err := q.db.QueryContext(ctx, coalesceColumns) + if err != nil { + return nil, err + } + defer rows.Close() + var items []CoalesceColumnsRow + for rows.Next() { + var i CoalesceColumnsRow + if err := rows.Scan(&i.Bar, &i.Bat, &i.Bar_2); 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/internal/endtoend/testdata/coalesce/sqlite/query.sql b/internal/endtoend/testdata/coalesce/sqlite/query.sql new file mode 100644 index 0000000000..022c1fb653 --- /dev/null +++ b/internal/endtoend/testdata/coalesce/sqlite/query.sql @@ -0,0 +1,9 @@ +CREATE TABLE foo (bar text, bat text not null); + +-- name: Coalesce :many +SELECT coalesce(bar, '') as login +FROM foo; + +-- name: CoalesceColumns :many +SELECT bar, bat, coalesce(bar, bat) +FROM foo; diff --git a/internal/endtoend/testdata/coalesce/sqlite/sqlc.json b/internal/endtoend/testdata/coalesce/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/coalesce/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/coalesce_as/sqlite/go/db.go b/internal/endtoend/testdata/coalesce_as/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/coalesce_as/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/coalesce_as/sqlite/go/models.go b/internal/endtoend/testdata/coalesce_as/sqlite/go/models.go new file mode 100644 index 0000000000..2e1941497e --- /dev/null +++ b/internal/endtoend/testdata/coalesce_as/sqlite/go/models.go @@ -0,0 +1,14 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Foo struct { + Bar sql.NullString + Baz sql.NullInt64 +} diff --git a/internal/endtoend/testdata/coalesce_as/sqlite/go/query.sql.go b/internal/endtoend/testdata/coalesce_as/sqlite/go/query.sql.go new file mode 100644 index 0000000000..44b78937f6 --- /dev/null +++ b/internal/endtoend/testdata/coalesce_as/sqlite/go/query.sql.go @@ -0,0 +1,45 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const sumBaz = `-- name: SumBaz :many +SELECT bar, coalesce(sum(baz), 0) as quantity +FROM foo +GROUP BY 1 +` + +type SumBazRow struct { + Bar sql.NullString + Quantity interface{} +} + +func (q *Queries) SumBaz(ctx context.Context) ([]SumBazRow, error) { + rows, err := q.db.QueryContext(ctx, sumBaz) + if err != nil { + return nil, err + } + defer rows.Close() + var items []SumBazRow + for rows.Next() { + var i SumBazRow + if err := rows.Scan(&i.Bar, &i.Quantity); 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/internal/endtoend/testdata/coalesce_as/sqlite/query.sql b/internal/endtoend/testdata/coalesce_as/sqlite/query.sql new file mode 100644 index 0000000000..b4e64b0bdd --- /dev/null +++ b/internal/endtoend/testdata/coalesce_as/sqlite/query.sql @@ -0,0 +1,9 @@ +CREATE TABLE foo ( + bar text, + baz integer +); + +-- name: SumBaz :many +SELECT bar, coalesce(sum(baz), 0) as quantity +FROM foo +GROUP BY 1; diff --git a/internal/endtoend/testdata/coalesce_as/sqlite/sqlc.json b/internal/endtoend/testdata/coalesce_as/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/coalesce_as/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index c92a08ae76..6f5a909a9c 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -166,27 +166,32 @@ func convertFuncContext(c *parser.Expr_functionContext) ast.Node { if name, ok := c.Function_name().(*parser.Function_nameContext); ok { funcName := strings.ToLower(name.GetText()) - var args []ast.Node + var argNodes []ast.Node for _, exp := range c.AllExpr() { - args = append(args, convert(exp)) + argNodes = append(argNodes, convert(exp)) } + args := &ast.List{Items: argNodes} - fn := &ast.FuncCall{ - Func: &ast.FuncName{ - Name: funcName, - }, - Funcname: &ast.List{ - Items: []ast.Node{ - NewIdentifer(funcName), + if funcName == "coalesce" { + return &ast.CoalesceExpr{ + Args: args, + } + } else { + return &ast.FuncCall{ + Func: &ast.FuncName{ + Name: funcName, }, - }, - AggStar: c.STAR() != nil, - Args: &ast.List{Items: args}, - AggOrder: &ast.List{}, - AggDistinct: c.DISTINCT_() != nil, + Funcname: &ast.List{ + Items: []ast.Node{ + NewIdentifer(funcName), + }, + }, + AggStar: c.STAR() != nil, + Args: args, + AggOrder: &ast.List{}, + AggDistinct: c.DISTINCT_() != nil, + } } - - return fn } return &ast.TODO{} From af12fa71d41dced8479b1891f3e3d63384df97e6 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 21:39:54 +0900 Subject: [PATCH 11/22] [sqlite] support 'bool' type --- internal/codegen/golang/sqlite_type.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/codegen/golang/sqlite_type.go b/internal/codegen/golang/sqlite_type.go index f26e533522..246febd3ef 100644 --- a/internal/codegen/golang/sqlite_type.go +++ b/internal/codegen/golang/sqlite_type.go @@ -32,7 +32,7 @@ func sqliteType(req *plugin.CodeGenRequest, col *plugin.Column) string { } return "sql.NullFloat64" - case "boolean": + case "boolean", "bool": if notNull { return "bool" } From 7649b8a898ed3c8fc8c445981757856628c7ed5a Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 22:22:36 +0900 Subject: [PATCH 12/22] [sqlite] support create/drop view --- internal/engine/sqlite/convert.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 6f5a909a9c..54eeae39e1 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -100,6 +100,27 @@ func convertCreate_table_stmtContext(c *parser.Create_table_stmtContext) ast.Nod return stmt } +func convertCreate_view_stmtContext(c *parser.Create_view_stmtContext) ast.Node { + viewName := c.View_name().GetText() + relation := &ast.RangeVar{ + Relname: &viewName, + } + + if c.Schema_name() != nil { + schemaName := c.Schema_name().GetText() + relation.Schemaname = &schemaName + } + + return &ast.ViewStmt{ + View: relation, + Aliases: &ast.List{}, + Query: convert(c.Select_stmt()), + Replace: false, + Options: &ast.List{}, + WithCheckOption: ast.ViewCheckOption(0), + } +} + func convertDelete_stmtContext(c *parser.Delete_stmtContext) ast.Node { if qualifiedName, ok := c.Qualified_table_name().(*parser.Qualified_table_nameContext); ok { @@ -137,7 +158,7 @@ func convertDelete_stmtContext(c *parser.Delete_stmtContext) ast.Node { } func convertDrop_stmtContext(c *parser.Drop_stmtContext) ast.Node { - if c.TABLE_() != nil { + if c.TABLE_() != nil || c.VIEW_() != nil { name := ast.TableName{ Name: c.Any_name().GetText(), } @@ -596,6 +617,9 @@ func convert(node node) ast.Node { case *parser.Create_table_stmtContext: return convertCreate_table_stmtContext(n) + case *parser.Create_view_stmtContext: + return convertCreate_view_stmtContext(n) + case *parser.Drop_stmtContext: return convertDrop_stmtContext(n) From df3db528a1423a834b17de1ddc787f77f656603b Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 22:23:45 +0900 Subject: [PATCH 13/22] add endtoend test for SQLite * column_as * comment_syntax * comparisons * create_view --- .../testdata/column_as/sqlite/go/db.go | 31 +++ .../testdata/column_as/sqlite/go/models.go | 11 + .../testdata/column_as/sqlite/go/query.sql.go | 37 ++++ .../testdata/column_as/sqlite/query.sql | 4 + .../testdata/column_as/sqlite/sqlc.json | 12 ++ .../testdata/comment_syntax/sqlite/go/db.go | 31 +++ .../comment_syntax/sqlite/go/models.go | 13 ++ .../comment_syntax/sqlite/go/query.sql.go | 33 +++ .../testdata/comment_syntax/sqlite/query.sql | 7 + .../testdata/comment_syntax/sqlite/sqlc.json | 12 ++ .../testdata/comparisons/sqlite/go/db.go | 31 +++ .../testdata/comparisons/sqlite/go/models.go | 11 + .../comparisons/sqlite/go/query.sql.go | 199 ++++++++++++++++++ .../testdata/comparisons/sqlite/query.sql | 31 +++ .../testdata/comparisons/sqlite/sqlc.json | 12 ++ .../testdata/create_view/sqlite/go/db.go | 31 +++ .../testdata/create_view/sqlite/go/models.go | 23 ++ .../create_view/sqlite/go/query.sql.go | 65 ++++++ .../testdata/create_view/sqlite/query.sql | 5 + .../testdata/create_view/sqlite/schema.sql | 11 + .../testdata/create_view/sqlite/sqlc.json | 12 ++ 21 files changed, 622 insertions(+) create mode 100644 internal/endtoend/testdata/column_as/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/column_as/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/column_as/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/column_as/sqlite/query.sql create mode 100644 internal/endtoend/testdata/column_as/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/comment_syntax/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/comment_syntax/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/comment_syntax/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/comment_syntax/sqlite/query.sql create mode 100644 internal/endtoend/testdata/comment_syntax/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/comparisons/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/comparisons/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/comparisons/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/comparisons/sqlite/query.sql create mode 100644 internal/endtoend/testdata/comparisons/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/create_view/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/create_view/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/create_view/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/create_view/sqlite/query.sql create mode 100644 internal/endtoend/testdata/create_view/sqlite/schema.sql create mode 100644 internal/endtoend/testdata/create_view/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/column_as/sqlite/go/db.go b/internal/endtoend/testdata/column_as/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/column_as/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/column_as/sqlite/go/models.go b/internal/endtoend/testdata/column_as/sqlite/go/models.go new file mode 100644 index 0000000000..893d9520c6 --- /dev/null +++ b/internal/endtoend/testdata/column_as/sqlite/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Foo struct { + Email string +} diff --git a/internal/endtoend/testdata/column_as/sqlite/go/query.sql.go b/internal/endtoend/testdata/column_as/sqlite/go/query.sql.go new file mode 100644 index 0000000000..5f0c926423 --- /dev/null +++ b/internal/endtoend/testdata/column_as/sqlite/go/query.sql.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const columnAs = `-- name: ColumnAs :many +SELECT email AS id FROM foo +` + +func (q *Queries) ColumnAs(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, columnAs) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/column_as/sqlite/query.sql b/internal/endtoend/testdata/column_as/sqlite/query.sql new file mode 100644 index 0000000000..4b055cc896 --- /dev/null +++ b/internal/endtoend/testdata/column_as/sqlite/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (email text not null); + +/* name: ColumnAs :many */ +SELECT email AS id FROM foo; diff --git a/internal/endtoend/testdata/column_as/sqlite/sqlc.json b/internal/endtoend/testdata/column_as/sqlite/sqlc.json new file mode 100644 index 0000000000..9aea94d599 --- /dev/null +++ b/internal/endtoend/testdata/column_as/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "_lemon", + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/comment_syntax/sqlite/go/db.go b/internal/endtoend/testdata/comment_syntax/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/comment_syntax/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/comment_syntax/sqlite/go/models.go b/internal/endtoend/testdata/comment_syntax/sqlite/go/models.go new file mode 100644 index 0000000000..523a17be93 --- /dev/null +++ b/internal/endtoend/testdata/comment_syntax/sqlite/go/models.go @@ -0,0 +1,13 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Foo struct { + Bar sql.NullString +} diff --git a/internal/endtoend/testdata/comment_syntax/sqlite/go/query.sql.go b/internal/endtoend/testdata/comment_syntax/sqlite/go/query.sql.go new file mode 100644 index 0000000000..f7f3dd1371 --- /dev/null +++ b/internal/endtoend/testdata/comment_syntax/sqlite/go/query.sql.go @@ -0,0 +1,33 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const doubleDash = `-- name: DoubleDash :one +SELECT bar FROM foo LIMIT 1 +` + +func (q *Queries) DoubleDash(ctx context.Context) (sql.NullString, error) { + row := q.db.QueryRowContext(ctx, doubleDash) + var bar sql.NullString + err := row.Scan(&bar) + return bar, err +} + +const slashStar = `-- name: SlashStar :one +SELECT bar FROM foo LIMIT 1 +` + +func (q *Queries) SlashStar(ctx context.Context) (sql.NullString, error) { + row := q.db.QueryRowContext(ctx, slashStar) + var bar sql.NullString + err := row.Scan(&bar) + return bar, err +} diff --git a/internal/endtoend/testdata/comment_syntax/sqlite/query.sql b/internal/endtoend/testdata/comment_syntax/sqlite/query.sql new file mode 100644 index 0000000000..2efd5e5a1c --- /dev/null +++ b/internal/endtoend/testdata/comment_syntax/sqlite/query.sql @@ -0,0 +1,7 @@ +CREATE TABLE foo (bar text); + +-- name: DoubleDash :one +SELECT * FROM foo LIMIT 1; + +/* name: SlashStar :one */ +SELECT * FROM foo LIMIT 1; diff --git a/internal/endtoend/testdata/comment_syntax/sqlite/sqlc.json b/internal/endtoend/testdata/comment_syntax/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/comment_syntax/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/comparisons/sqlite/go/db.go b/internal/endtoend/testdata/comparisons/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/comparisons/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/comparisons/sqlite/go/models.go b/internal/endtoend/testdata/comparisons/sqlite/go/models.go new file mode 100644 index 0000000000..a3136ad600 --- /dev/null +++ b/internal/endtoend/testdata/comparisons/sqlite/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Bar struct { + ID int64 +} diff --git a/internal/endtoend/testdata/comparisons/sqlite/go/query.sql.go b/internal/endtoend/testdata/comparisons/sqlite/go/query.sql.go new file mode 100644 index 0000000000..ee96df81e7 --- /dev/null +++ b/internal/endtoend/testdata/comparisons/sqlite/go/query.sql.go @@ -0,0 +1,199 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const alsoNotEqual = `-- name: AlsoNotEqual :many +SELECT count(*) <> 0 FROM bar +` + +func (q *Queries) AlsoNotEqual(ctx context.Context) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, alsoNotEqual) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var column_1 bool + if err := rows.Scan(&column_1); err != nil { + return nil, err + } + items = append(items, column_1) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const equal = `-- name: Equal :many +SELECT count(*) = 0 FROM bar +` + +func (q *Queries) Equal(ctx context.Context) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, equal) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var column_1 bool + if err := rows.Scan(&column_1); err != nil { + return nil, err + } + items = append(items, column_1) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const greaterThan = `-- name: GreaterThan :many +SELECT count(*) > 0 FROM bar +` + +func (q *Queries) GreaterThan(ctx context.Context) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, greaterThan) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var column_1 bool + if err := rows.Scan(&column_1); err != nil { + return nil, err + } + items = append(items, column_1) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const greaterThanOrEqual = `-- name: GreaterThanOrEqual :many +SELECT count(*) >= 0 FROM bar +` + +func (q *Queries) GreaterThanOrEqual(ctx context.Context) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, greaterThanOrEqual) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var column_1 bool + if err := rows.Scan(&column_1); err != nil { + return nil, err + } + items = append(items, column_1) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const lessThan = `-- name: LessThan :many +SELECT count(*) < 0 FROM bar +` + +func (q *Queries) LessThan(ctx context.Context) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, lessThan) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var column_1 bool + if err := rows.Scan(&column_1); err != nil { + return nil, err + } + items = append(items, column_1) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const lessThanOrEqual = `-- name: LessThanOrEqual :many +SELECT count(*) <= 0 FROM bar +` + +func (q *Queries) LessThanOrEqual(ctx context.Context) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, lessThanOrEqual) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var column_1 bool + if err := rows.Scan(&column_1); err != nil { + return nil, err + } + items = append(items, column_1) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const notEqual = `-- name: NotEqual :many +SELECT count(*) != 0 FROM bar +` + +func (q *Queries) NotEqual(ctx context.Context) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, notEqual) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var column_1 bool + if err := rows.Scan(&column_1); err != nil { + return nil, err + } + items = append(items, column_1) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/comparisons/sqlite/query.sql b/internal/endtoend/testdata/comparisons/sqlite/query.sql new file mode 100644 index 0000000000..27d7741d02 --- /dev/null +++ b/internal/endtoend/testdata/comparisons/sqlite/query.sql @@ -0,0 +1,31 @@ +-- Comparison Functions and Operators +-- https://www.postgresql.org/docs/current/functions-comparison.html + +CREATE TABLE bar (id integer not null primary key autoincrement); + +-- name: GreaterThan :many +SELECT count(*) > 0 FROM bar; + +-- name: LessThan :many +SELECT count(*) < 0 FROM bar; + +-- name: GreaterThanOrEqual :many +SELECT count(*) >= 0 FROM bar; + +-- name: LessThanOrEqual :many +SELECT count(*) <= 0 FROM bar; + +-- name: NotEqual :many +SELECT count(*) != 0 FROM bar; + +-- name: AlsoNotEqual :many +SELECT count(*) <> 0 FROM bar; + +-- name: Equal :many +SELECT count(*) = 0 FROM bar; + + + + + + diff --git a/internal/endtoend/testdata/comparisons/sqlite/sqlc.json b/internal/endtoend/testdata/comparisons/sqlite/sqlc.json new file mode 100644 index 0000000000..d1c5d38e4b --- /dev/null +++ b/internal/endtoend/testdata/comparisons/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "name": "querytest", + "engine": "_lemon", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/create_view/sqlite/go/db.go b/internal/endtoend/testdata/create_view/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/create_view/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/create_view/sqlite/go/models.go b/internal/endtoend/testdata/create_view/sqlite/go/models.go new file mode 100644 index 0000000000..a881e640dd --- /dev/null +++ b/internal/endtoend/testdata/create_view/sqlite/go/models.go @@ -0,0 +1,23 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type FirstView struct { + Val string +} + +type Foo struct { + Val string + Val2 sql.NullInt64 +} + +type SecondView struct { + Val string + Val2 sql.NullInt64 +} diff --git a/internal/endtoend/testdata/create_view/sqlite/go/query.sql.go b/internal/endtoend/testdata/create_view/sqlite/go/query.sql.go new file mode 100644 index 0000000000..6da2185eb1 --- /dev/null +++ b/internal/endtoend/testdata/create_view/sqlite/go/query.sql.go @@ -0,0 +1,65 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const getFirst = `-- name: GetFirst :many +SELECT val FROM first_view +` + +func (q *Queries) GetFirst(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, getFirst) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var val string + if err := rows.Scan(&val); err != nil { + return nil, err + } + items = append(items, val) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getSecond = `-- name: GetSecond :many +SELECT val, val2 FROM second_view WHERE val2 = ? +` + +func (q *Queries) GetSecond(ctx context.Context, val2 sql.NullInt64) ([]SecondView, error) { + rows, err := q.db.QueryContext(ctx, getSecond, val2) + if err != nil { + return nil, err + } + defer rows.Close() + var items []SecondView + for rows.Next() { + var i SecondView + if err := rows.Scan(&i.Val, &i.Val2); 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/internal/endtoend/testdata/create_view/sqlite/query.sql b/internal/endtoend/testdata/create_view/sqlite/query.sql new file mode 100644 index 0000000000..c94fc58556 --- /dev/null +++ b/internal/endtoend/testdata/create_view/sqlite/query.sql @@ -0,0 +1,5 @@ +-- name: GetFirst :many +SELECT * FROM first_view; + +-- name: GetSecond :many +SELECT * FROM second_view WHERE val2 = ?; diff --git a/internal/endtoend/testdata/create_view/sqlite/schema.sql b/internal/endtoend/testdata/create_view/sqlite/schema.sql new file mode 100644 index 0000000000..246f73e0b7 --- /dev/null +++ b/internal/endtoend/testdata/create_view/sqlite/schema.sql @@ -0,0 +1,11 @@ +CREATE TABLE foo (val text not null); + +CREATE VIEW first_view AS SELECT * FROM foo; +CREATE VIEW second_view AS SELECT * FROM foo; +CREATE VIEW third_view AS SELECT * FROM foo; + +ALTER TABLE foo ADD COLUMN val2 integer; +DROP VIEW second_view; +CREATE VIEW second_view AS SELECT * FROM foo; + +DROP VIEW third_view; diff --git a/internal/endtoend/testdata/create_view/sqlite/sqlc.json b/internal/endtoend/testdata/create_view/sqlite/sqlc.json new file mode 100644 index 0000000000..39d417df7f --- /dev/null +++ b/internal/endtoend/testdata/create_view/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "_lemon", + "path": "go", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file From afd39438c9b2a22c35f60879cfa5299a3b3aec19 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Mon, 13 Jun 2022 23:04:10 +0900 Subject: [PATCH 14/22] [sqlite] update datatype mapping --- internal/codegen/golang/sqlite_type.go | 8 +- .../data_type_boolean/sqlite/db/db.go | 31 +++++++ .../data_type_boolean/sqlite/db/models.go | 19 +++++ .../data_type_boolean/sqlite/db/query.sql.go | 64 +++++++++++++++ .../data_type_boolean/sqlite/query.sql | 17 ++++ .../data_type_boolean/sqlite/sqlc.json | 11 +++ .../testdata/datatype/sqlite/go/db.go | 31 +++++++ .../testdata/datatype/sqlite/go/models.go | 80 +++++++++++++++++++ .../datatype/sqlite/sql/character.sql | 23 ++++++ .../testdata/datatype/sqlite/sql/datetime.sql | 13 +++ .../testdata/datatype/sqlite/sql/numeric.sql | 37 +++++++++ .../testdata/datatype/sqlite/sql/query.sql | 1 + .../testdata/datatype/sqlite/sqlc.json | 12 +++ 13 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 internal/endtoend/testdata/data_type_boolean/sqlite/db/db.go create mode 100644 internal/endtoend/testdata/data_type_boolean/sqlite/db/models.go create mode 100644 internal/endtoend/testdata/data_type_boolean/sqlite/db/query.sql.go create mode 100644 internal/endtoend/testdata/data_type_boolean/sqlite/query.sql create mode 100644 internal/endtoend/testdata/data_type_boolean/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/datatype/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/datatype/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/datatype/sqlite/sql/character.sql create mode 100644 internal/endtoend/testdata/datatype/sqlite/sql/datetime.sql create mode 100644 internal/endtoend/testdata/datatype/sqlite/sql/numeric.sql create mode 100644 internal/endtoend/testdata/datatype/sqlite/sql/query.sql create mode 100644 internal/endtoend/testdata/datatype/sqlite/sqlc.json diff --git a/internal/codegen/golang/sqlite_type.go b/internal/codegen/golang/sqlite_type.go index 246febd3ef..3667367b83 100644 --- a/internal/codegen/golang/sqlite_type.go +++ b/internal/codegen/golang/sqlite_type.go @@ -14,7 +14,7 @@ func sqliteType(req *plugin.CodeGenRequest, col *plugin.Column) string { switch dt { - case "int", "integer", "tinyint", "smallint", "mediumint", "bigint", "unsignedbigint", "int2", "int8", "numeric", "decimal": + case "int", "integer", "tinyint", "smallint", "mediumint", "bigint", "unsignedbigint", "int2", "int8": if notNull { return "int64" } @@ -64,6 +64,12 @@ func sqliteType(req *plugin.CodeGenRequest, col *plugin.Column) string { } return "sql.NullString" + case strings.HasPrefix(dt, "decimal"), dt == "numeric": + if notNull { + return "float64" + } + return "sql.NullFloat64" + default: log.Printf("unknown SQLite type: %s\n", dt) return "interface{}" diff --git a/internal/endtoend/testdata/data_type_boolean/sqlite/db/db.go b/internal/endtoend/testdata/data_type_boolean/sqlite/db/db.go new file mode 100644 index 0000000000..f30b89ec02 --- /dev/null +++ b/internal/endtoend/testdata/data_type_boolean/sqlite/db/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package db + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/data_type_boolean/sqlite/db/models.go b/internal/endtoend/testdata/data_type_boolean/sqlite/db/models.go new file mode 100644 index 0000000000..0a87ab09f6 --- /dev/null +++ b/internal/endtoend/testdata/data_type_boolean/sqlite/db/models.go @@ -0,0 +1,19 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package db + +import ( + "database/sql" +) + +type Bar struct { + ColA sql.NullBool + ColB sql.NullBool +} + +type Foo struct { + ColA bool + ColB bool +} diff --git a/internal/endtoend/testdata/data_type_boolean/sqlite/db/query.sql.go b/internal/endtoend/testdata/data_type_boolean/sqlite/db/query.sql.go new file mode 100644 index 0000000000..6b890e5079 --- /dev/null +++ b/internal/endtoend/testdata/data_type_boolean/sqlite/db/query.sql.go @@ -0,0 +1,64 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package db + +import ( + "context" +) + +const listBar = `-- name: ListBar :many +SELECT col_a, col_b FROM bar +` + +func (q *Queries) ListBar(ctx context.Context) ([]Bar, error) { + rows, err := q.db.QueryContext(ctx, listBar) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Bar + for rows.Next() { + var i Bar + if err := rows.Scan(&i.ColA, &i.ColB); 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 listFoo = `-- name: ListFoo :many +SELECT col_a, col_b FROM foo +` + +func (q *Queries) ListFoo(ctx context.Context) ([]Foo, error) { + rows, err := q.db.QueryContext(ctx, listFoo) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Foo + for rows.Next() { + var i Foo + if err := rows.Scan(&i.ColA, &i.ColB); 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/internal/endtoend/testdata/data_type_boolean/sqlite/query.sql b/internal/endtoend/testdata/data_type_boolean/sqlite/query.sql new file mode 100644 index 0000000000..53ad078373 --- /dev/null +++ b/internal/endtoend/testdata/data_type_boolean/sqlite/query.sql @@ -0,0 +1,17 @@ +CREATE TABLE foo +( + col_a BOOL NOT NULL, + col_b BOOLEAN NOT NULL +); + +-- name: ListFoo :many +SELECT * FROM foo; + +CREATE TABLE bar +( + col_a BOOL, + col_b BOOLEAN +); + +-- name: ListBar :many +SELECT * FROM bar; diff --git a/internal/endtoend/testdata/data_type_boolean/sqlite/sqlc.json b/internal/endtoend/testdata/data_type_boolean/sqlite/sqlc.json new file mode 100644 index 0000000000..5499ab0a68 --- /dev/null +++ b/internal/endtoend/testdata/data_type_boolean/sqlite/sqlc.json @@ -0,0 +1,11 @@ +{ + "version": "1", + "packages": [ + { + "path": "db", + "engine": "_lemon", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/datatype/sqlite/go/db.go b/internal/endtoend/testdata/datatype/sqlite/go/db.go new file mode 100644 index 0000000000..8fbb3c811f --- /dev/null +++ b/internal/endtoend/testdata/datatype/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package datatype + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/datatype/sqlite/go/models.go b/internal/endtoend/testdata/datatype/sqlite/go/models.go new file mode 100644 index 0000000000..65180c48ad --- /dev/null +++ b/internal/endtoend/testdata/datatype/sqlite/go/models.go @@ -0,0 +1,80 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package datatype + +import ( + "database/sql" + "time" +) + +type DtCharacter struct { + A sql.NullString + B sql.NullString + C sql.NullString + D sql.NullString + E sql.NullString + F sql.NullString + G sql.NullString + H sql.NullString +} + +type DtCharacterNotNull struct { + A string + B string + C string + D string + E string + F string + G string + H string +} + +type DtDatetime struct { + A sql.NullTime + B sql.NullTime + C sql.NullTime +} + +type DtDatetimeNotNull struct { + A time.Time + B time.Time + C time.Time +} + +type DtNumeric struct { + A sql.NullInt64 + B sql.NullInt64 + C sql.NullInt64 + D sql.NullInt64 + E sql.NullInt64 + F sql.NullInt64 + G sql.NullInt64 + H sql.NullInt64 + I sql.NullInt64 + J sql.NullFloat64 + K sql.NullFloat64 + L sql.NullFloat64 + M sql.NullFloat64 + N sql.NullFloat64 + O sql.NullFloat64 +} + +type DtNumericNotNull struct { + A int64 + B int64 + C int64 + D int64 + E int64 + F int64 + G int64 + H int64 + I int64 + J float64 + K float64 + L float64 + M float64 + N float64 + O float64 +} diff --git a/internal/endtoend/testdata/datatype/sqlite/sql/character.sql b/internal/endtoend/testdata/datatype/sqlite/sql/character.sql new file mode 100644 index 0000000000..d4b728bdab --- /dev/null +++ b/internal/endtoend/testdata/datatype/sqlite/sql/character.sql @@ -0,0 +1,23 @@ +-- Character Types +-- https://www.sqlite.org/datatype3.html +CREATE TABLE dt_character ( + a CHARACTER(32), + b VARCHAR(32), + c VARYING CHARACTER(32), + d NCHAR(32), + e NATIVE CHARACTER(32), + f NVARCHAR(32), + g TEXT, + h CLOB +); + +CREATE TABLE dt_character_not_null ( + a CHARACTER(32) NOT NULL, + b VARCHAR(32) NOT NULL, + c VARYING CHARACTER(32) NOT NULL, + d NCHAR(32) NOT NULL, + e NATIVE CHARACTER(32) NOT NULL, + f NVARCHAR(32) NOT NULL, + g TEXT NOT NULL, + h CLOB NOT NULL +); diff --git a/internal/endtoend/testdata/datatype/sqlite/sql/datetime.sql b/internal/endtoend/testdata/datatype/sqlite/sql/datetime.sql new file mode 100644 index 0000000000..6008cf6ae6 --- /dev/null +++ b/internal/endtoend/testdata/datatype/sqlite/sql/datetime.sql @@ -0,0 +1,13 @@ +-- Date/Time Types +-- https://www.sqlite.org/datatype3.html +CREATE TABLE dt_datetime ( + a DATE, + b DATETIME, + c TIMESTAMP +); + +CREATE TABLE dt_datetime_not_null ( + a DATE NOT NULL, + b DATETIME NOT NULL, + c TIMESTAMP NOT NULL +); diff --git a/internal/endtoend/testdata/datatype/sqlite/sql/numeric.sql b/internal/endtoend/testdata/datatype/sqlite/sql/numeric.sql new file mode 100644 index 0000000000..a85b4f295b --- /dev/null +++ b/internal/endtoend/testdata/datatype/sqlite/sql/numeric.sql @@ -0,0 +1,37 @@ +-- Numeric Types +-- https://www.sqlite.org/datatype3.html +CREATE TABLE dt_numeric ( + a INT, + b INTEGER, + c TINYINT, + d SMALLINT, + e MEDIUMINT, + f BIGINT, + g UNSIGNED BIG INT, + h INT2, + i INT8, + j REAL, + k DOUBLE, + l DOUBLE PRECISION, + m FLOAT, + n NUMERIC, + o DECIMAL(10,5) +); + +CREATE TABLE dt_numeric_not_null ( + a INT NOT NULL, + b INTEGER NOT NULL, + c TINYINT NOT NULL, + d SMALLINT NOT NULL, + e MEDIUMINT NOT NULL, + f BIGINT NOT NULL, + g UNSIGNED BIG INT NOT NULL, + h INT2 NOT NULL, + i INT8 NOT NULL, + j REAL NOT NULL, + k DOUBLE NOT NULL, + l DOUBLE PRECISION NOT NULL, + m FLOAT NOT NULL, + n NUMERIC NOT NULL, + o DECIMAL(10,5) NOT NULL +); diff --git a/internal/endtoend/testdata/datatype/sqlite/sql/query.sql b/internal/endtoend/testdata/datatype/sqlite/sql/query.sql new file mode 100644 index 0000000000..e0ac49d1ec --- /dev/null +++ b/internal/endtoend/testdata/datatype/sqlite/sql/query.sql @@ -0,0 +1 @@ +SELECT 1; diff --git a/internal/endtoend/testdata/datatype/sqlite/sqlc.json b/internal/endtoend/testdata/datatype/sqlite/sqlc.json new file mode 100644 index 0000000000..8b6fbfd05a --- /dev/null +++ b/internal/endtoend/testdata/datatype/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "datatype", + "schema": "sql/", + "queries": "sql/" + } + ] +} \ No newline at end of file From ad2d9e34d9560ed2425b48340eec37080624cd4f Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Wed, 15 Jun 2022 21:14:09 +0900 Subject: [PATCH 15/22] [sqlite] support insert_select --- internal/engine/sqlite/convert.go | 90 +++++++++++++++---------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 54eeae39e1..3aed281389 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -281,6 +281,7 @@ func convertMultiSelect_stmtContext(c multiselect) ast.Node { FromClause: &ast.List{Items: tables}, TargetList: &ast.List{Items: cols}, WhereClause: where, + ValuesLists: &ast.List{}, } } @@ -480,64 +481,61 @@ func convertParam(c *parser.Expr_bindContext) ast.Node { } func convertInsert_stmtContext(c *parser.Insert_stmtContext) ast.Node { - if c == nil { - return nil + tableName := c.Table_name().GetText() + rel := &ast.RangeVar{ + Relname: &tableName, } - values := c.AllExpr() - if len(values) == 0 { - // TODO: add INSERT WITH SELECT support - return &ast.TODO{} + if c.Schema_name() != nil { + schemaName := c.Schema_name().GetText() + rel.Schemaname = &schemaName } - tableName := c.Table_name().GetText() - // we MUST have the columns in the update, - // otherwise the parser does not give ANY context, - // on "expression groups" - columns := convertCols(c.AllColumn_name()) - insertStmt := &ast.InsertStmt{ - Relation: &ast.RangeVar{ - Relname: &tableName, - }, - Cols: columns, - ReturningList: &ast.List{}, - SelectStmt: convertSelectStmt(values, len(c.AllColumn_name())), + if c.Table_alias() != nil { + tableAlias := c.Table_alias().GetText() + rel.Alias = &ast.Alias{ + Aliasname: &tableAlias, + } } - return insertStmt -} -func convertSelectStmt(values []parser.IExprContext, columns int) ast.Node { - valueList := &ast.List{Items: []ast.Node{}} - // the sqlite parse will give us values in a single slice - // so INSERT INTO a (b, c) VALUES (?, ?), (?, ?); - // will produce a 4 element slice. sqlite forces - // each column to have an expression so - // INSERT INTO a (b, c) VALUES (?); is invalid - // even if c is nullable - if columns == 0 { - columns = len(values) + insert := &ast.InsertStmt{ + Relation: rel, + Cols: convertColumnNames(c.AllColumn_name()), + ReturningList: &ast.List{}, } - for i := 0; i < len(values); i++ { - inner := &ast.List{Items: []ast.Node{}} - for ; i < columns; i++ { - inner.Items = append(inner.Items, convert(values[i])) + + if ss, ok := convert(c.Select_stmt()).(*ast.SelectStmt); ok { + ss.ValuesLists = &ast.List{} + insert.SelectStmt = ss + } else { + insert.SelectStmt = &ast.SelectStmt{ + FromClause: &ast.List{}, + TargetList: &ast.List{}, + ValuesLists: convertExprLists(c.AllExpr()), } - valueList.Items = append(valueList.Items, inner) } - return &ast.SelectStmt{ - FromClause: &ast.List{}, - TargetList: &ast.List{}, - ValuesLists: valueList, + + return insert +} + +func convertExprLists(lists []parser.IExprContext) *ast.List { + list := &ast.List{Items: []ast.Node{}} + n := len(lists) + inner := &ast.List{Items: []ast.Node{}} + for i := 0; i < n; i++ { + inner.Items = append(inner.Items, convert(lists[i])) } + list.Items = append(list.Items, inner) + return list } -func convertCols(columns []parser.IColumn_nameContext) *ast.List { - out := &ast.List{} - for _, c := range columns { - colName := c.GetText() - out.Items = append(out.Items, &ast.ResTarget{ - Name: &colName, +func convertColumnNames(cols []parser.IColumn_nameContext) *ast.List { + list := &ast.List{Items: []ast.Node{}} + for _, c := range cols { + name := c.GetText() + list.Items = append(list.Items, &ast.ResTarget{ + Name: &name, }) } - return out + return list } func convertTablesOrSubquery(tableOrSubquery []parser.ITable_or_subqueryContext) []ast.Node { From b8aef7c2ee09d2047428dbace4bc0faee584e97f Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Wed, 15 Jun 2022 21:15:23 +0900 Subject: [PATCH 16/22] add endtoend test for SQLite * identical_tables * inflection * insert_select * insert_select_invalid * insert_values --- .../ddl_create_trigger/sqlite/go/models.go | 5 + .../testdata/identical_tables/sqlite/go/db.go | 31 +++++++ .../identical_tables/sqlite/go/models.go | 15 +++ .../identical_tables/sqlite/go/query.sql.go | 37 ++++++++ .../identical_tables/sqlite/query.sql | 5 + .../identical_tables/sqlite/sqlc.json | 12 +++ .../testdata/inflection/sqlite/go/db.go | 31 +++++++ .../testdata/inflection/sqlite/go/models.go | 19 ++++ .../inflection/sqlite/go/query.sql.go | 91 +++++++++++++++++++ .../testdata/inflection/sqlite/query.sql | 12 +++ .../testdata/inflection/sqlite/sqlc.json | 12 +++ .../testdata/insert_select/sqlite/go/db.go | 31 +++++++ .../insert_select/sqlite/go/models.go | 17 ++++ .../insert_select/sqlite/go/query.sql.go | 26 ++++++ .../testdata/insert_select/sqlite/query.sql | 7 ++ .../testdata/insert_select/sqlite/sqlc.json | 12 +++ .../insert_select_invalid/sqlite/query.sql | 5 + .../insert_select_invalid/sqlite/sqlc.json | 12 +++ .../insert_select_invalid/sqlite/stderr.txt | 2 + .../testdata/insert_values/sqlite/go/db.go | 31 +++++++ .../insert_values/sqlite/go/models.go | 14 +++ .../insert_values/sqlite/go/query.sql.go | 25 +++++ .../testdata/insert_values/sqlite/query.sql | 4 + .../testdata/insert_values/sqlite/sqlc.json | 12 +++ 24 files changed, 468 insertions(+) create mode 100644 internal/endtoend/testdata/identical_tables/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/identical_tables/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/identical_tables/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/identical_tables/sqlite/query.sql create mode 100644 internal/endtoend/testdata/identical_tables/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/inflection/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/inflection/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/inflection/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/inflection/sqlite/query.sql create mode 100644 internal/endtoend/testdata/inflection/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/insert_select/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/insert_select/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/insert_select/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/insert_select/sqlite/query.sql create mode 100644 internal/endtoend/testdata/insert_select/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/insert_select_invalid/sqlite/query.sql create mode 100644 internal/endtoend/testdata/insert_select_invalid/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/insert_select_invalid/sqlite/stderr.txt create mode 100644 internal/endtoend/testdata/insert_values/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/insert_values/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/insert_values/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/insert_values/sqlite/query.sql create mode 100644 internal/endtoend/testdata/insert_values/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/ddl_create_trigger/sqlite/go/models.go b/internal/endtoend/testdata/ddl_create_trigger/sqlite/go/models.go index c1c9a4d337..fd0a7ad229 100644 --- a/internal/endtoend/testdata/ddl_create_trigger/sqlite/go/models.go +++ b/internal/endtoend/testdata/ddl_create_trigger/sqlite/go/models.go @@ -13,3 +13,8 @@ type Customer struct { CustName sql.NullString CustAddr sql.NullString } + +type CustomerAddress struct { + CustID int64 + CustAddr sql.NullString +} diff --git a/internal/endtoend/testdata/identical_tables/sqlite/go/db.go b/internal/endtoend/testdata/identical_tables/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/identical_tables/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/identical_tables/sqlite/go/models.go b/internal/endtoend/testdata/identical_tables/sqlite/go/models.go new file mode 100644 index 0000000000..5a99c0db9a --- /dev/null +++ b/internal/endtoend/testdata/identical_tables/sqlite/go/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Bar struct { + ID string +} + +type Foo struct { + ID string +} diff --git a/internal/endtoend/testdata/identical_tables/sqlite/go/query.sql.go b/internal/endtoend/testdata/identical_tables/sqlite/go/query.sql.go new file mode 100644 index 0000000000..40605b12a5 --- /dev/null +++ b/internal/endtoend/testdata/identical_tables/sqlite/go/query.sql.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const identicalTable = `-- name: IdenticalTable :many +SELECT id FROM foo +` + +func (q *Queries) IdenticalTable(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, identicalTable) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/identical_tables/sqlite/query.sql b/internal/endtoend/testdata/identical_tables/sqlite/query.sql new file mode 100644 index 0000000000..c995d0f183 --- /dev/null +++ b/internal/endtoend/testdata/identical_tables/sqlite/query.sql @@ -0,0 +1,5 @@ +CREATE TABLE foo (id text not null); +CREATE TABLE bar (id text not null); + +-- name: IdenticalTable :many +SELECT * FROM foo; diff --git a/internal/endtoend/testdata/identical_tables/sqlite/sqlc.json b/internal/endtoend/testdata/identical_tables/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/identical_tables/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/inflection/sqlite/go/db.go b/internal/endtoend/testdata/inflection/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/inflection/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/inflection/sqlite/go/models.go b/internal/endtoend/testdata/inflection/sqlite/go/models.go new file mode 100644 index 0000000000..5a25cada01 --- /dev/null +++ b/internal/endtoend/testdata/inflection/sqlite/go/models.go @@ -0,0 +1,19 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Campus struct { + ID string +} + +type ProductMetum struct { + ID string +} + +type Student struct { + ID string +} diff --git a/internal/endtoend/testdata/inflection/sqlite/go/query.sql.go b/internal/endtoend/testdata/inflection/sqlite/go/query.sql.go new file mode 100644 index 0000000000..1b551497a8 --- /dev/null +++ b/internal/endtoend/testdata/inflection/sqlite/go/query.sql.go @@ -0,0 +1,91 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const listCampuses = `-- name: ListCampuses :many +SELECT id FROM campus +` + +func (q *Queries) ListCampuses(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, listCampuses) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMetadata = `-- name: ListMetadata :many +SELECT id FROM product_meta +` + +func (q *Queries) ListMetadata(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, listMetadata) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listStudents = `-- name: ListStudents :many +SELECT id FROM students +` + +func (q *Queries) ListStudents(ctx context.Context) ([]string, error) { + rows, err := q.db.QueryContext(ctx, listStudents) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return nil, err + } + items = append(items, id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/inflection/sqlite/query.sql b/internal/endtoend/testdata/inflection/sqlite/query.sql new file mode 100644 index 0000000000..23f1cb26d9 --- /dev/null +++ b/internal/endtoend/testdata/inflection/sqlite/query.sql @@ -0,0 +1,12 @@ +CREATE TABLE campus (id text not null); +CREATE TABLE students (id text not null); +CREATE TABLE product_meta (id text not null); + +/* name: ListCampuses :many */ +SELECT * FROM campus; + +/* name: ListStudents :many */ +SELECT * FROM students; + +/* name: ListMetadata :many */ +SELECT * FROM product_meta; diff --git a/internal/endtoend/testdata/inflection/sqlite/sqlc.json b/internal/endtoend/testdata/inflection/sqlite/sqlc.json new file mode 100644 index 0000000000..9aea94d599 --- /dev/null +++ b/internal/endtoend/testdata/inflection/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "_lemon", + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/insert_select/sqlite/go/db.go b/internal/endtoend/testdata/insert_select/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/insert_select/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/insert_select/sqlite/go/models.go b/internal/endtoend/testdata/insert_select/sqlite/go/models.go new file mode 100644 index 0000000000..ac408cae57 --- /dev/null +++ b/internal/endtoend/testdata/insert_select/sqlite/go/models.go @@ -0,0 +1,17 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Bar struct { + Name string + Ready bool +} + +type Foo struct { + Name string + Meta string +} diff --git a/internal/endtoend/testdata/insert_select/sqlite/go/query.sql.go b/internal/endtoend/testdata/insert_select/sqlite/go/query.sql.go new file mode 100644 index 0000000000..a287db7c9f --- /dev/null +++ b/internal/endtoend/testdata/insert_select/sqlite/go/query.sql.go @@ -0,0 +1,26 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const insertSelect = `-- name: InsertSelect :exec +INSERT INTO foo (name, meta) +SELECT name, ? +FROM bar WHERE ready = ? +` + +type InsertSelectParams struct { + Meta string + Ready bool +} + +func (q *Queries) InsertSelect(ctx context.Context, arg InsertSelectParams) error { + _, err := q.db.ExecContext(ctx, insertSelect, arg.Meta, arg.Ready) + return err +} diff --git a/internal/endtoend/testdata/insert_select/sqlite/query.sql b/internal/endtoend/testdata/insert_select/sqlite/query.sql new file mode 100644 index 0000000000..69aee7f0e9 --- /dev/null +++ b/internal/endtoend/testdata/insert_select/sqlite/query.sql @@ -0,0 +1,7 @@ +CREATE TABLE bar (name text not null, ready bool not null); +CREATE TABLE foo (name text not null, meta text not null); + +/* name: InsertSelect :exec */ +INSERT INTO foo (name, meta) +SELECT name, ? +FROM bar WHERE ready = ?; diff --git a/internal/endtoend/testdata/insert_select/sqlite/sqlc.json b/internal/endtoend/testdata/insert_select/sqlite/sqlc.json new file mode 100644 index 0000000000..9aea94d599 --- /dev/null +++ b/internal/endtoend/testdata/insert_select/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "_lemon", + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/insert_select_invalid/sqlite/query.sql b/internal/endtoend/testdata/insert_select_invalid/sqlite/query.sql new file mode 100644 index 0000000000..cfd90fe55d --- /dev/null +++ b/internal/endtoend/testdata/insert_select_invalid/sqlite/query.sql @@ -0,0 +1,5 @@ +CREATE TABLE foo (bar text); + +-- name: InsertFoo :exec +INSERT INTO foo (bar) +SELECT 1, ?, ?; diff --git a/internal/endtoend/testdata/insert_select_invalid/sqlite/sqlc.json b/internal/endtoend/testdata/insert_select_invalid/sqlite/sqlc.json new file mode 100644 index 0000000000..9aea94d599 --- /dev/null +++ b/internal/endtoend/testdata/insert_select_invalid/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "_lemon", + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/insert_select_invalid/sqlite/stderr.txt b/internal/endtoend/testdata/insert_select_invalid/sqlite/stderr.txt new file mode 100644 index 0000000000..063b2a149a --- /dev/null +++ b/internal/endtoend/testdata/insert_select_invalid/sqlite/stderr.txt @@ -0,0 +1,2 @@ +# package querytest +query.sql:4:1: INSERT has more expressions than target columns diff --git a/internal/endtoend/testdata/insert_values/sqlite/go/db.go b/internal/endtoend/testdata/insert_values/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/insert_values/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/insert_values/sqlite/go/models.go b/internal/endtoend/testdata/insert_values/sqlite/go/models.go new file mode 100644 index 0000000000..dfa095d2fb --- /dev/null +++ b/internal/endtoend/testdata/insert_values/sqlite/go/models.go @@ -0,0 +1,14 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Foo struct { + A sql.NullString + B sql.NullInt64 +} diff --git a/internal/endtoend/testdata/insert_values/sqlite/go/query.sql.go b/internal/endtoend/testdata/insert_values/sqlite/go/query.sql.go new file mode 100644 index 0000000000..40e1842df0 --- /dev/null +++ b/internal/endtoend/testdata/insert_values/sqlite/go/query.sql.go @@ -0,0 +1,25 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const insertValues = `-- name: InsertValues :exec +INSERT INTO foo (a, b) VALUES (?, ?) +` + +type InsertValuesParams struct { + A sql.NullString + B sql.NullInt64 +} + +func (q *Queries) InsertValues(ctx context.Context, arg InsertValuesParams) error { + _, err := q.db.ExecContext(ctx, insertValues, arg.A, arg.B) + return err +} diff --git a/internal/endtoend/testdata/insert_values/sqlite/query.sql b/internal/endtoend/testdata/insert_values/sqlite/query.sql new file mode 100644 index 0000000000..78a29ccd39 --- /dev/null +++ b/internal/endtoend/testdata/insert_values/sqlite/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (a text, b integer); + +/* name: InsertValues :exec */ +INSERT INTO foo (a, b) VALUES (?, ?); diff --git a/internal/endtoend/testdata/insert_values/sqlite/sqlc.json b/internal/endtoend/testdata/insert_values/sqlite/sqlc.json new file mode 100644 index 0000000000..9aea94d599 --- /dev/null +++ b/internal/endtoend/testdata/insert_values/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "_lemon", + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file From d2352bfa401d883d593c2b2ddf8181e3429a1976 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Thu, 16 Jun 2022 01:27:04 +0900 Subject: [PATCH 17/22] [sqlite] better select statement support * support `GROUP BY` and `HAVING` * support `ORDER BY` * support wildcard(`*`) column with table name --- internal/engine/sqlite/convert.go | 87 +++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 3aed281389..1c9fb38923 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -258,10 +258,13 @@ func convertComparison(c *parser.Expr_comparisonContext) ast.Node { return aExpr } -func convertMultiSelect_stmtContext(c multiselect) ast.Node { +func convertMultiSelect_stmtContext(c *parser.Select_stmtContext) ast.Node { var tables []ast.Node var cols []ast.Node var where ast.Node + var groupBy = &ast.List{Items: []ast.Node{}} + var having ast.Node + for _, icore := range c.AllSelect_core() { core, ok := icore.(*parser.Select_coreContext) if !ok { @@ -270,18 +273,43 @@ func convertMultiSelect_stmtContext(c multiselect) ast.Node { cols = append(cols, getCols(core)...) tables = append(tables, getTables(core)...) + i := 0 if core.WHERE_() != nil { - if core.Expr(0) != nil { - where = convert(core.Expr(0)) + where = convert(core.Expr(i)) + i++ + } + + if core.GROUP_() != nil { + n := len(core.AllExpr()) - i + if core.HAVING_() != nil { + n-- + } + + for i < n { + groupBy.Items = append(groupBy.Items, convert(core.Expr(i))) + i++ + } + + if core.HAVING_() != nil { + having = convert(core.Expr(i)) + i++ } } } + window := &ast.List{Items: []ast.Node{}} + if c.Order_by_stmt() != nil { + window.Items = append(window.Items, convert(c.Order_by_stmt())) + } + return &ast.SelectStmt{ - FromClause: &ast.List{Items: tables}, - TargetList: &ast.List{Items: cols}, - WhereClause: where, - ValuesLists: &ast.List{}, + FromClause: &ast.List{Items: tables}, + TargetList: &ast.List{Items: cols}, + WhereClause: where, + GroupClause: groupBy, + HavingClause: having, + WindowClause: window, + ValuesLists: &ast.List{}, } } @@ -313,14 +341,7 @@ func getCols(core *parser.Select_coreContext) []ast.Node { iexpr := col.Expr() switch { case col.STAR() != nil: - val = &ast.ColumnRef{ - Fields: &ast.List{ - Items: []ast.Node{ - &ast.A_Star{}, - }, - }, - Location: col.GetStart().GetStart(), - } + val = convertWildCardField(col) case iexpr != nil: val = convert(iexpr) } @@ -340,6 +361,39 @@ func getCols(core *parser.Select_coreContext) []ast.Node { return cols } +func convertWildCardField(c *parser.Result_columnContext) *ast.ColumnRef { + items := []ast.Node{} + if c.Table_name() != nil { + items = append(items, NewIdentifer(c.Table_name().GetText())) + } + items = append(items, &ast.A_Star{}) + + return &ast.ColumnRef{ + Fields: &ast.List{ + Items: items, + }, + Location: c.GetStart().GetStart(), + } +} + +func convertOrderby_stmtContext(c parser.IOrder_by_stmtContext) ast.Node { + if orderBy, ok := c.(*parser.Order_by_stmtContext); ok { + list := &ast.List{Items: []ast.Node{}} + for _, o := range orderBy.AllOrdering_term() { + term, ok := o.(*parser.Ordering_termContext) + if !ok { + continue + } + list.Items = append(list.Items, &ast.CaseExpr{ + Xpr: convert(term.Expr()), + Location: term.Expr().GetStart().GetStart(), + }) + } + return list + } + return &ast.TODO{} +} + func convertSql_stmtContext(n *parser.Sql_stmtContext) ast.Node { if stmt := n.Alter_table_stmt(); stmt != nil { return convert(stmt) @@ -655,6 +709,9 @@ func convert(node node) ast.Node { case *parser.Insert_stmtContext: return convertInsert_stmtContext(n) + case *parser.Order_by_stmtContext: + return convertOrderby_stmtContext(n) + case *parser.Select_stmtContext: return convertMultiSelect_stmtContext(n) From c205d59572ff946c0f0302e5b29b49a7aaf300ca Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Thu, 16 Jun 2022 01:30:30 +0900 Subject: [PATCH 18/22] add endtoend test for SQLite * invalid_group_by_reference * invalid_table_alias * join_alias * join_from * join_left * join_left_same_table * join_table_name * join_two_tables * join_where_clause --- .../sqlite/query.sql | 11 + .../sqlite/sqlc.json | 12 + .../sqlite/stderr.txt | 2 + .../invalid_table_alias/sqlite/query.sql | 12 + .../invalid_table_alias/sqlite/sqlc.json | 12 + .../invalid_table_alias/sqlite/stderr.txt | 2 + .../testdata/join_alias/sqlite/go/db.go | 31 ++ .../testdata/join_alias/sqlite/go/models.go | 18 + .../join_alias/sqlite/go/query.sql.go | 82 ++++ .../testdata/join_alias/sqlite/query.sql | 14 + .../testdata/join_alias/sqlite/sqlc.json | 12 + .../testdata/join_from/sqlite/go/db.go | 31 ++ .../testdata/join_from/sqlite/go/models.go | 15 + .../testdata/join_from/sqlite/go/query.sql.go | 37 ++ .../testdata/join_from/sqlite/query.sql | 5 + .../testdata/join_from/sqlite/sqlc.json | 12 + .../testdata/join_left/sqlite/go/db.go | 31 ++ .../testdata/join_left/sqlite/go/models.go | 60 +++ .../testdata/join_left/sqlite/go/query.sql.go | 433 ++++++++++++++++++ .../testdata/join_left/sqlite/query.sql | 110 +++++ .../testdata/join_left/sqlite/sqlc.json | 12 + .../join_left_same_table/sqlite/go/db.go | 31 ++ .../join_left_same_table/sqlite/go/models.go | 15 + .../sqlite/go/query.sql.go | 55 +++ .../join_left_same_table/sqlite/query.sql | 14 + .../join_left_same_table/sqlite/sqlc.json | 12 + .../testdata/join_table_name/sqlite/go/db.go | 31 ++ .../join_table_name/sqlite/go/models.go | 18 + .../join_table_name/sqlite/go/query.sql.go | 29 ++ .../testdata/join_table_name/sqlite/query.sql | 8 + .../testdata/join_table_name/sqlite/sqlc.json | 12 + .../testdata/join_two_tables/sqlite/go/db.go | 31 ++ .../join_two_tables/sqlite/go/models.go | 20 + .../join_two_tables/sqlite/go/query.sql.go | 40 ++ .../testdata/join_two_tables/sqlite/query.sql | 9 + .../testdata/join_two_tables/sqlite/sqlc.json | 12 + .../join_where_clause/sqlite/go/db.go | 31 ++ .../join_where_clause/sqlite/go/models.go | 16 + .../join_where_clause/sqlite/go/query.sql.go | 40 ++ .../join_where_clause/sqlite/query.sql | 8 + .../join_where_clause/sqlite/sqlc.json | 12 + 41 files changed, 1398 insertions(+) create mode 100644 internal/endtoend/testdata/invalid_group_by_reference/sqlite/query.sql create mode 100644 internal/endtoend/testdata/invalid_group_by_reference/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/invalid_group_by_reference/sqlite/stderr.txt create mode 100644 internal/endtoend/testdata/invalid_table_alias/sqlite/query.sql create mode 100644 internal/endtoend/testdata/invalid_table_alias/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/invalid_table_alias/sqlite/stderr.txt create mode 100644 internal/endtoend/testdata/join_alias/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/join_alias/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/join_alias/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_alias/sqlite/query.sql create mode 100644 internal/endtoend/testdata/join_alias/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/join_from/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/join_from/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/join_from/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_from/sqlite/query.sql create mode 100644 internal/endtoend/testdata/join_from/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/join_left/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/join_left/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/join_left/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_left/sqlite/query.sql create mode 100644 internal/endtoend/testdata/join_left/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/join_left_same_table/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/join_left_same_table/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/join_left_same_table/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_left_same_table/sqlite/query.sql create mode 100644 internal/endtoend/testdata/join_left_same_table/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/join_table_name/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/join_table_name/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/join_table_name/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_table_name/sqlite/query.sql create mode 100644 internal/endtoend/testdata/join_table_name/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/join_two_tables/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/join_two_tables/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/join_two_tables/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_two_tables/sqlite/query.sql create mode 100644 internal/endtoend/testdata/join_two_tables/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/join_where_clause/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/join_where_clause/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/join_where_clause/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/join_where_clause/sqlite/query.sql create mode 100644 internal/endtoend/testdata/join_where_clause/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/invalid_group_by_reference/sqlite/query.sql b/internal/endtoend/testdata/invalid_group_by_reference/sqlite/query.sql new file mode 100644 index 0000000000..41ed0cf32c --- /dev/null +++ b/internal/endtoend/testdata/invalid_group_by_reference/sqlite/query.sql @@ -0,0 +1,11 @@ +CREATE TABLE authors ( + id integer NOT NULL PRIMARY KEY AUTOINCREMENT, + name text NOT NULL, + bio text, + UNIQUE(name) +); + +-- name: ListAuthors :many +SELECT * +FROM authors +GROUP BY invalid_reference; diff --git a/internal/endtoend/testdata/invalid_group_by_reference/sqlite/sqlc.json b/internal/endtoend/testdata/invalid_group_by_reference/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/invalid_group_by_reference/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/invalid_group_by_reference/sqlite/stderr.txt b/internal/endtoend/testdata/invalid_group_by_reference/sqlite/stderr.txt new file mode 100644 index 0000000000..18a7d6bd0f --- /dev/null +++ b/internal/endtoend/testdata/invalid_group_by_reference/sqlite/stderr.txt @@ -0,0 +1,2 @@ +# package querytest +query.sql:9:1: column reference "invalid_reference" not found diff --git a/internal/endtoend/testdata/invalid_table_alias/sqlite/query.sql b/internal/endtoend/testdata/invalid_table_alias/sqlite/query.sql new file mode 100644 index 0000000000..4c06fa07a0 --- /dev/null +++ b/internal/endtoend/testdata/invalid_table_alias/sqlite/query.sql @@ -0,0 +1,12 @@ +-- https://github.com/kyleconroy/sqlc/issues/437 +CREATE TABLE authors ( + id INT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + bio text +); + +-- name: GetAuthor :one +SELECT * +FROM authors a +WHERE p.id = ? +LIMIT 1; diff --git a/internal/endtoend/testdata/invalid_table_alias/sqlite/sqlc.json b/internal/endtoend/testdata/invalid_table_alias/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/invalid_table_alias/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/invalid_table_alias/sqlite/stderr.txt b/internal/endtoend/testdata/invalid_table_alias/sqlite/stderr.txt new file mode 100644 index 0000000000..d4c26d650f --- /dev/null +++ b/internal/endtoend/testdata/invalid_table_alias/sqlite/stderr.txt @@ -0,0 +1,2 @@ +# package querytest +query.sql:9:1: table alias "p" does not exist diff --git a/internal/endtoend/testdata/join_alias/sqlite/go/db.go b/internal/endtoend/testdata/join_alias/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/join_alias/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_alias/sqlite/go/models.go b/internal/endtoend/testdata/join_alias/sqlite/go/models.go new file mode 100644 index 0000000000..063b519683 --- /dev/null +++ b/internal/endtoend/testdata/join_alias/sqlite/go/models.go @@ -0,0 +1,18 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Bar struct { + ID int64 + Title sql.NullString +} + +type Foo struct { + ID int64 +} diff --git a/internal/endtoend/testdata/join_alias/sqlite/go/query.sql.go b/internal/endtoend/testdata/join_alias/sqlite/go/query.sql.go new file mode 100644 index 0000000000..6bfd3eb914 --- /dev/null +++ b/internal/endtoend/testdata/join_alias/sqlite/go/query.sql.go @@ -0,0 +1,82 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const aliasExpand = `-- name: AliasExpand :many +SELECT f.id, b.id, title +FROM foo f +JOIN bar b ON b.id = f.id +WHERE f.id = ? +` + +type AliasExpandRow struct { + ID int64 + ID_2 int64 + Title sql.NullString +} + +func (q *Queries) AliasExpand(ctx context.Context, id int64) ([]AliasExpandRow, error) { + rows, err := q.db.QueryContext(ctx, aliasExpand, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AliasExpandRow + for rows.Next() { + var i AliasExpandRow + if err := rows.Scan(&i.ID, &i.ID_2, &i.Title); 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 aliasJoin = `-- name: AliasJoin :many +SELECT f.id, b.title +FROM foo f +JOIN bar b ON b.id = f.id +WHERE f.id = ? +` + +type AliasJoinRow struct { + ID int64 + Title sql.NullString +} + +func (q *Queries) AliasJoin(ctx context.Context, id int64) ([]AliasJoinRow, error) { + rows, err := q.db.QueryContext(ctx, aliasJoin, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AliasJoinRow + for rows.Next() { + var i AliasJoinRow + if err := rows.Scan(&i.ID, &i.Title); 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/internal/endtoend/testdata/join_alias/sqlite/query.sql b/internal/endtoend/testdata/join_alias/sqlite/query.sql new file mode 100644 index 0000000000..c8d3044460 --- /dev/null +++ b/internal/endtoend/testdata/join_alias/sqlite/query.sql @@ -0,0 +1,14 @@ +CREATE TABLE foo (id integer not null); +CREATE TABLE bar (id integer not null references foo(id), title text); + +-- name: AliasJoin :many +SELECT f.id, b.title +FROM foo f +JOIN bar b ON b.id = f.id +WHERE f.id = ?; + +-- name: AliasExpand :many +SELECT * +FROM foo f +JOIN bar b ON b.id = f.id +WHERE f.id = ?; diff --git a/internal/endtoend/testdata/join_alias/sqlite/sqlc.json b/internal/endtoend/testdata/join_alias/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/join_alias/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/join_from/sqlite/go/db.go b/internal/endtoend/testdata/join_from/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/join_from/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_from/sqlite/go/models.go b/internal/endtoend/testdata/join_from/sqlite/go/models.go new file mode 100644 index 0000000000..6c251d6aab --- /dev/null +++ b/internal/endtoend/testdata/join_from/sqlite/go/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Bar struct { + Login string +} + +type Foo struct { + Email string +} diff --git a/internal/endtoend/testdata/join_from/sqlite/go/query.sql.go b/internal/endtoend/testdata/join_from/sqlite/go/query.sql.go new file mode 100644 index 0000000000..80a09c4220 --- /dev/null +++ b/internal/endtoend/testdata/join_from/sqlite/go/query.sql.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const multiFrom = `-- name: MultiFrom :many +SELECT email FROM bar, foo WHERE login = ? +` + +func (q *Queries) MultiFrom(ctx context.Context, login string) ([]string, error) { + rows, err := q.db.QueryContext(ctx, multiFrom, login) + if err != nil { + return nil, err + } + defer rows.Close() + var items []string + for rows.Next() { + var email string + if err := rows.Scan(&email); err != nil { + return nil, err + } + items = append(items, email) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/join_from/sqlite/query.sql b/internal/endtoend/testdata/join_from/sqlite/query.sql new file mode 100644 index 0000000000..588d859578 --- /dev/null +++ b/internal/endtoend/testdata/join_from/sqlite/query.sql @@ -0,0 +1,5 @@ +CREATE TABLE foo (email text not null); +CREATE TABLE bar (login text not null); + +-- name: MultiFrom :many +SELECT email FROM bar, foo WHERE login = ?; diff --git a/internal/endtoend/testdata/join_from/sqlite/sqlc.json b/internal/endtoend/testdata/join_from/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/join_from/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/join_left/sqlite/go/db.go b/internal/endtoend/testdata/join_left/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/join_left/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_left/sqlite/go/models.go b/internal/endtoend/testdata/join_left/sqlite/go/models.go new file mode 100644 index 0000000000..725603c989 --- /dev/null +++ b/internal/endtoend/testdata/join_left/sqlite/go/models.go @@ -0,0 +1,60 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" + "time" +) + +type Author struct { + ID int64 + Name string + ParentID sql.NullInt64 +} + +type City struct { + CityID int64 + MayorID int64 +} + +type Mayor struct { + MayorID int64 + FullName string +} + +type Medium struct { + MediaID int64 + MediaCreatedAt time.Time + MediaHash string + MediaDirectory string + MediaAuthorID int64 + MediaWidth int64 + MediaHeight int64 +} + +type SuperAuthor struct { + SuperID int64 + SuperName string + SuperParentID sql.NullInt64 +} + +type User struct { + UserID int64 + CityID sql.NullInt64 +} + +type Users2 struct { + UserID int64 + UserNickname string + UserEmail string + UserDisplayName string + UserPassword sql.NullString + UserGoogleID sql.NullString + UserAppleID sql.NullString + UserBio string + UserCreatedAt time.Time + UserAvatarID sql.NullInt64 +} diff --git a/internal/endtoend/testdata/join_left/sqlite/go/query.sql.go b/internal/endtoend/testdata/join_left/sqlite/go/query.sql.go new file mode 100644 index 0000000000..4624ae78ff --- /dev/null +++ b/internal/endtoend/testdata/join_left/sqlite/go/query.sql.go @@ -0,0 +1,433 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" + "time" +) + +const allAuthors = `-- name: AllAuthors :many +SELECT a.id, a.name, a.parent_id, p.id, p.name, p.parent_id +FROM authors AS a + LEFT JOIN authors AS p + ON a.parent_id = p.id +` + +type AllAuthorsRow struct { + ID int64 + Name string + ParentID sql.NullInt64 + ID_2 int64 + Name_2 string + ParentID_2 sql.NullInt64 +} + +func (q *Queries) AllAuthors(ctx context.Context) ([]AllAuthorsRow, error) { + rows, err := q.db.QueryContext(ctx, allAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AllAuthorsRow + for rows.Next() { + var i AllAuthorsRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ParentID, + &i.ID_2, + &i.Name_2, + &i.ParentID_2, + ); 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 allAuthorsAliases = `-- name: AllAuthorsAliases :many +SELECT a.id, a.name, a.parent_id, p.id, p.name, p.parent_id +FROM authors AS a + LEFT JOIN authors AS p + ON a.parent_id = p.id +` + +type AllAuthorsAliasesRow struct { + ID int64 + Name string + ParentID sql.NullInt64 + ID_2 int64 + Name_2 string + ParentID_2 sql.NullInt64 +} + +func (q *Queries) AllAuthorsAliases(ctx context.Context) ([]AllAuthorsAliasesRow, error) { + rows, err := q.db.QueryContext(ctx, allAuthorsAliases) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AllAuthorsAliasesRow + for rows.Next() { + var i AllAuthorsAliasesRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ParentID, + &i.ID_2, + &i.Name_2, + &i.ParentID_2, + ); 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 allAuthorsAliases2 = `-- name: AllAuthorsAliases2 :many +SELECT a.id, a.name, a.parent_id, p.id, p.name, p.parent_id +FROM authors AS a + LEFT JOIN authors AS p + ON a.parent_id = p.id +` + +type AllAuthorsAliases2Row struct { + ID int64 + Name string + ParentID sql.NullInt64 + ID_2 int64 + Name_2 string + ParentID_2 sql.NullInt64 +} + +func (q *Queries) AllAuthorsAliases2(ctx context.Context) ([]AllAuthorsAliases2Row, error) { + rows, err := q.db.QueryContext(ctx, allAuthorsAliases2) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AllAuthorsAliases2Row + for rows.Next() { + var i AllAuthorsAliases2Row + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ParentID, + &i.ID_2, + &i.Name_2, + &i.ParentID_2, + ); 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 allSuperAuthors = `-- name: AllSuperAuthors :many +SELECT id, name, parent_id, super_id, super_name, super_parent_id +FROM authors + LEFT JOIN super_authors + ON authors.parent_id = super_authors.super_id +` + +type AllSuperAuthorsRow struct { + ID int64 + Name string + ParentID sql.NullInt64 + SuperID int64 + SuperName string + SuperParentID sql.NullInt64 +} + +func (q *Queries) AllSuperAuthors(ctx context.Context) ([]AllSuperAuthorsRow, error) { + rows, err := q.db.QueryContext(ctx, allSuperAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AllSuperAuthorsRow + for rows.Next() { + var i AllSuperAuthorsRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ParentID, + &i.SuperID, + &i.SuperName, + &i.SuperParentID, + ); 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 allSuperAuthorsAliases = `-- name: AllSuperAuthorsAliases :many +SELECT id, name, parent_id, super_id, super_name, super_parent_id +FROM authors AS a + LEFT JOIN super_authors AS sa + ON a.parent_id = sa.super_id +` + +type AllSuperAuthorsAliasesRow struct { + ID int64 + Name string + ParentID sql.NullInt64 + SuperID int64 + SuperName string + SuperParentID sql.NullInt64 +} + +func (q *Queries) AllSuperAuthorsAliases(ctx context.Context) ([]AllSuperAuthorsAliasesRow, error) { + rows, err := q.db.QueryContext(ctx, allSuperAuthorsAliases) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AllSuperAuthorsAliasesRow + for rows.Next() { + var i AllSuperAuthorsAliasesRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ParentID, + &i.SuperID, + &i.SuperName, + &i.SuperParentID, + ); 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 allSuperAuthorsAliases2 = `-- name: AllSuperAuthorsAliases2 :many +SELECT a.id, a.name, a.parent_id, sa.super_id, sa.super_name, sa.super_parent_id +FROM authors AS a + LEFT JOIN super_authors AS sa + ON a.parent_id = sa.super_id +` + +type AllSuperAuthorsAliases2Row struct { + ID int64 + Name string + ParentID sql.NullInt64 + SuperID int64 + SuperName string + SuperParentID sql.NullInt64 +} + +func (q *Queries) AllSuperAuthorsAliases2(ctx context.Context) ([]AllSuperAuthorsAliases2Row, error) { + rows, err := q.db.QueryContext(ctx, allSuperAuthorsAliases2) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AllSuperAuthorsAliases2Row + for rows.Next() { + var i AllSuperAuthorsAliases2Row + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ParentID, + &i.SuperID, + &i.SuperName, + &i.SuperParentID, + ); 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 getMayors = `-- name: GetMayors :many +SELECT + user_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +INNER JOIN mayors USING (mayor_id) +` + +type GetMayorsRow struct { + UserID int64 + FullName string +} + +func (q *Queries) GetMayors(ctx context.Context) ([]GetMayorsRow, error) { + rows, err := q.db.QueryContext(ctx, getMayors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetMayorsRow + for rows.Next() { + var i GetMayorsRow + if err := rows.Scan(&i.UserID, &i.FullName); 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 getMayorsOptional = `-- name: GetMayorsOptional :many +SELECT + user_id, + cities.city_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +LEFT JOIN mayors USING (mayor_id) +` + +type GetMayorsOptionalRow struct { + UserID int64 + CityID int64 + FullName string +} + +func (q *Queries) GetMayorsOptional(ctx context.Context) ([]GetMayorsOptionalRow, error) { + rows, err := q.db.QueryContext(ctx, getMayorsOptional) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetMayorsOptionalRow + for rows.Next() { + var i GetMayorsOptionalRow + if err := rows.Scan(&i.UserID, &i.CityID, &i.FullName); 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 getSuggestedUsersByID = `-- name: GetSuggestedUsersByID :many +SELECT DISTINCT u.user_id, u.user_nickname, u.user_email, u.user_display_name, u.user_password, u.user_google_id, u.user_apple_id, u.user_bio, u.user_created_at, u.user_avatar_id, m.media_id, m.media_created_at, m.media_hash, m.media_directory, m.media_author_id, m.media_width, m.media_height +FROM users_2 AS u + LEFT JOIN media AS m + ON u.user_avatar_id = m.media_id +WHERE u.user_id != @user_id +` + +type GetSuggestedUsersByIDRow struct { + UserID int64 + UserNickname string + UserEmail string + UserDisplayName string + UserPassword sql.NullString + UserGoogleID sql.NullString + UserAppleID sql.NullString + UserBio string + UserCreatedAt time.Time + UserAvatarID sql.NullInt64 + MediaID int64 + MediaCreatedAt time.Time + MediaHash string + MediaDirectory string + MediaAuthorID int64 + MediaWidth int64 + MediaHeight int64 +} + +func (q *Queries) GetSuggestedUsersByID(ctx context.Context, userID int64) ([]GetSuggestedUsersByIDRow, error) { + rows, err := q.db.QueryContext(ctx, getSuggestedUsersByID, userID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetSuggestedUsersByIDRow + for rows.Next() { + var i GetSuggestedUsersByIDRow + if err := rows.Scan( + &i.UserID, + &i.UserNickname, + &i.UserEmail, + &i.UserDisplayName, + &i.UserPassword, + &i.UserGoogleID, + &i.UserAppleID, + &i.UserBio, + &i.UserCreatedAt, + &i.UserAvatarID, + &i.MediaID, + &i.MediaCreatedAt, + &i.MediaHash, + &i.MediaDirectory, + &i.MediaAuthorID, + &i.MediaWidth, + &i.MediaHeight, + ); 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/internal/endtoend/testdata/join_left/sqlite/query.sql b/internal/endtoend/testdata/join_left/sqlite/query.sql new file mode 100644 index 0000000000..a88764bf5e --- /dev/null +++ b/internal/endtoend/testdata/join_left/sqlite/query.sql @@ -0,0 +1,110 @@ +-- https://github.com/kyleconroy/sqlc/issues/604 +CREATE TABLE users ( + user_id INT PRIMARY KEY, + city_id INT -- nullable +); +CREATE TABLE cities ( + city_id INT PRIMARY KEY, + mayor_id INT NOT NULL +); +CREATE TABLE mayors ( + mayor_id INT PRIMARY KEY, + full_name TEXT NOT NULL +); + +-- name: GetMayors :many +SELECT + user_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +INNER JOIN mayors USING (mayor_id); + +-- name: GetMayorsOptional :many +SELECT + user_id, + cities.city_id, + mayors.full_name +FROM users +LEFT JOIN cities USING (city_id) +LEFT JOIN mayors USING (mayor_id); + +-- https://github.com/kyleconroy/sqlc/issues/1334 +CREATE TABLE authors ( + id INT PRIMARY KEY, + name TEXT NOT NULL, + parent_id INT -- nullable +); + +CREATE TABLE super_authors ( + super_id INT PRIMARY KEY, + super_name TEXT NOT NULL, + super_parent_id INT -- nullable +); + +-- name: AllAuthors :many +SELECT * +FROM authors AS a + LEFT JOIN authors AS p + ON a.parent_id = p.id; + +-- name: AllAuthorsAliases :many +SELECT * +FROM authors AS a + LEFT JOIN authors AS p + ON a.parent_id = p.id; + +-- name: AllSuperAuthors :many +SELECT * +FROM authors + LEFT JOIN super_authors + ON authors.parent_id = super_authors.super_id; + +-- name: AllAuthorsAliases2 :many +SELECT a.*, p.* +FROM authors AS a + LEFT JOIN authors AS p + ON a.parent_id = p.id; + +-- name: AllSuperAuthorsAliases :many +SELECT * +FROM authors AS a + LEFT JOIN super_authors AS sa + ON a.parent_id = sa.super_id; + +-- name: AllSuperAuthorsAliases2 :many +SELECT a.*, sa.* +FROM authors AS a + LEFT JOIN super_authors AS sa + ON a.parent_id = sa.super_id; + +-- https://github.com/kyleconroy/sqlc/issues/1334 +CREATE TABLE users_2 ( + user_id INT PRIMARY KEY, + user_nickname VARCHAR(30) UNIQUE NOT NULL, + user_email TEXT UNIQUE NOT NULL, + user_display_name TEXT NOT NULL, + user_password TEXT , + user_google_id TEXT UNIQUE , + user_apple_id TEXT UNIQUE , + user_bio VARCHAR(160) NOT NULL DEFAULT '', + user_created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + user_avatar_id INT UNIQUE +); + +CREATE TABLE media ( + media_id INT PRIMARY KEY, + media_created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + media_hash TEXT NOT NULL, + media_directory TEXT NOT NULL, + media_author_id INT NOT NULL, + media_width INT NOT NULL, + media_height INT NOT NULL +); + +-- name: GetSuggestedUsersByID :many +SELECT DISTINCT u.*, m.* +FROM users_2 AS u + LEFT JOIN media AS m + ON u.user_avatar_id = m.media_id +WHERE u.user_id != @user_id; diff --git a/internal/endtoend/testdata/join_left/sqlite/sqlc.json b/internal/endtoend/testdata/join_left/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/join_left/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/join_left_same_table/sqlite/go/db.go b/internal/endtoend/testdata/join_left_same_table/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/join_left_same_table/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_left_same_table/sqlite/go/models.go b/internal/endtoend/testdata/join_left_same_table/sqlite/go/models.go new file mode 100644 index 0000000000..8187783ce6 --- /dev/null +++ b/internal/endtoend/testdata/join_left_same_table/sqlite/go/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Author struct { + ID int64 + Name string + ParentID sql.NullInt64 +} diff --git a/internal/endtoend/testdata/join_left_same_table/sqlite/go/query.sql.go b/internal/endtoend/testdata/join_left_same_table/sqlite/go/query.sql.go new file mode 100644 index 0000000000..92dcedd768 --- /dev/null +++ b/internal/endtoend/testdata/join_left_same_table/sqlite/go/query.sql.go @@ -0,0 +1,55 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const allAuthors = `-- name: AllAuthors :many +SELECT a.id, + a.name, + p.id as alias_id, + p.name as alias_name +FROM authors AS a + LEFT JOIN authors AS p + ON (authors.parent_id = p.id) +` + +type AllAuthorsRow struct { + ID int64 + Name string + AliasID int64 + AliasName string +} + +func (q *Queries) AllAuthors(ctx context.Context) ([]AllAuthorsRow, error) { + rows, err := q.db.QueryContext(ctx, allAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AllAuthorsRow + for rows.Next() { + var i AllAuthorsRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.AliasID, + &i.AliasName, + ); 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/internal/endtoend/testdata/join_left_same_table/sqlite/query.sql b/internal/endtoend/testdata/join_left_same_table/sqlite/query.sql new file mode 100644 index 0000000000..56d3757635 --- /dev/null +++ b/internal/endtoend/testdata/join_left_same_table/sqlite/query.sql @@ -0,0 +1,14 @@ +CREATE TABLE authors ( + id INT NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + parent_id INT +); + +-- name: AllAuthors :many +SELECT a.id, + a.name, + p.id as alias_id, + p.name as alias_name +FROM authors AS a + LEFT JOIN authors AS p + ON (authors.parent_id = p.id); diff --git a/internal/endtoend/testdata/join_left_same_table/sqlite/sqlc.json b/internal/endtoend/testdata/join_left_same_table/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/join_left_same_table/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/join_table_name/sqlite/go/db.go b/internal/endtoend/testdata/join_table_name/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/join_table_name/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_table_name/sqlite/go/models.go b/internal/endtoend/testdata/join_table_name/sqlite/go/models.go new file mode 100644 index 0000000000..fad9c78581 --- /dev/null +++ b/internal/endtoend/testdata/join_table_name/sqlite/go/models.go @@ -0,0 +1,18 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Bar struct { + ID int64 +} + +type Foo struct { + ID int64 + Bar sql.NullInt64 +} diff --git a/internal/endtoend/testdata/join_table_name/sqlite/go/query.sql.go b/internal/endtoend/testdata/join_table_name/sqlite/go/query.sql.go new file mode 100644 index 0000000000..bb5702cca7 --- /dev/null +++ b/internal/endtoend/testdata/join_table_name/sqlite/go/query.sql.go @@ -0,0 +1,29 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const tableName = `-- name: TableName :one +SELECT foo.id +FROM foo +JOIN bar ON foo.bar = bar.id +WHERE bar.id = ? AND foo.id = ? +` + +type TableNameParams struct { + ID int64 + ID_2 int64 +} + +func (q *Queries) TableName(ctx context.Context, arg TableNameParams) (int64, error) { + row := q.db.QueryRowContext(ctx, tableName, arg.ID, arg.ID_2) + var id int64 + err := row.Scan(&id) + return id, err +} diff --git a/internal/endtoend/testdata/join_table_name/sqlite/query.sql b/internal/endtoend/testdata/join_table_name/sqlite/query.sql new file mode 100644 index 0000000000..b3cb5f12a2 --- /dev/null +++ b/internal/endtoend/testdata/join_table_name/sqlite/query.sql @@ -0,0 +1,8 @@ +CREATE TABLE bar (id integer not null); +CREATE TABLE foo (id integer not null, bar integer references bar(id)); + +-- name: TableName :one +SELECT foo.id +FROM foo +JOIN bar ON foo.bar = bar.id +WHERE bar.id = ? AND foo.id = ?; diff --git a/internal/endtoend/testdata/join_table_name/sqlite/sqlc.json b/internal/endtoend/testdata/join_table_name/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/join_table_name/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/join_two_tables/sqlite/go/db.go b/internal/endtoend/testdata/join_two_tables/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/join_two_tables/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_two_tables/sqlite/go/models.go b/internal/endtoend/testdata/join_two_tables/sqlite/go/models.go new file mode 100644 index 0000000000..852d86d7c4 --- /dev/null +++ b/internal/endtoend/testdata/join_two_tables/sqlite/go/models.go @@ -0,0 +1,20 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Bar struct { + ID int64 +} + +type Baz struct { + ID int64 +} + +type Foo struct { + BarID int64 + BazID int64 +} diff --git a/internal/endtoend/testdata/join_two_tables/sqlite/go/query.sql.go b/internal/endtoend/testdata/join_two_tables/sqlite/go/query.sql.go new file mode 100644 index 0000000000..fc2d8b30b2 --- /dev/null +++ b/internal/endtoend/testdata/join_two_tables/sqlite/go/query.sql.go @@ -0,0 +1,40 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const twoJoins = `-- name: TwoJoins :many +SELECT foo.bar_id, foo.baz_id +FROM foo +JOIN bar ON bar.id = bar_id +JOIN baz ON baz.id = baz_id +` + +func (q *Queries) TwoJoins(ctx context.Context) ([]Foo, error) { + rows, err := q.db.QueryContext(ctx, twoJoins) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Foo + for rows.Next() { + var i Foo + if err := rows.Scan(&i.BarID, &i.BazID); 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/internal/endtoend/testdata/join_two_tables/sqlite/query.sql b/internal/endtoend/testdata/join_two_tables/sqlite/query.sql new file mode 100644 index 0000000000..bd5fd19f50 --- /dev/null +++ b/internal/endtoend/testdata/join_two_tables/sqlite/query.sql @@ -0,0 +1,9 @@ +CREATE TABLE foo (bar_id integer not null, baz_id integer not null); +CREATE TABLE bar (id integer not null); +CREATE TABLE baz (id integer not null); + +-- name: TwoJoins :many +SELECT foo.* +FROM foo +JOIN bar ON bar.id = bar_id +JOIN baz ON baz.id = baz_id; diff --git a/internal/endtoend/testdata/join_two_tables/sqlite/sqlc.json b/internal/endtoend/testdata/join_two_tables/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/join_two_tables/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/join_where_clause/sqlite/go/db.go b/internal/endtoend/testdata/join_where_clause/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/join_where_clause/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_where_clause/sqlite/go/models.go b/internal/endtoend/testdata/join_where_clause/sqlite/go/models.go new file mode 100644 index 0000000000..b626555450 --- /dev/null +++ b/internal/endtoend/testdata/join_where_clause/sqlite/go/models.go @@ -0,0 +1,16 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Bar struct { + ID int64 + Owner string +} + +type Foo struct { + Barid int64 +} diff --git a/internal/endtoend/testdata/join_where_clause/sqlite/go/query.sql.go b/internal/endtoend/testdata/join_where_clause/sqlite/go/query.sql.go new file mode 100644 index 0000000000..07a1db395b --- /dev/null +++ b/internal/endtoend/testdata/join_where_clause/sqlite/go/query.sql.go @@ -0,0 +1,40 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const joinWhereClause = `-- name: JoinWhereClause :many +SELECT foo.barid +FROM foo +JOIN bar ON bar.id = barid +WHERE owner = ? +` + +func (q *Queries) JoinWhereClause(ctx context.Context, owner string) ([]int64, error) { + rows, err := q.db.QueryContext(ctx, joinWhereClause, owner) + if err != nil { + return nil, err + } + defer rows.Close() + var items []int64 + for rows.Next() { + var barid int64 + if err := rows.Scan(&barid); err != nil { + return nil, err + } + items = append(items, barid) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/join_where_clause/sqlite/query.sql b/internal/endtoend/testdata/join_where_clause/sqlite/query.sql new file mode 100644 index 0000000000..1a95284227 --- /dev/null +++ b/internal/endtoend/testdata/join_where_clause/sqlite/query.sql @@ -0,0 +1,8 @@ +CREATE TABLE foo (barid integer not null); +CREATE TABLE bar (id integer not null, owner text not null); + +-- name: JoinWhereClause :many +SELECT foo.* +FROM foo +JOIN bar ON bar.id = barid +WHERE owner = ?; diff --git a/internal/endtoend/testdata/join_where_clause/sqlite/sqlc.json b/internal/endtoend/testdata/join_where_clause/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/join_where_clause/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file From bc7d67250dc6714c2b7b6c3e42282be7747a7c1c Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Thu, 16 Jun 2022 23:40:24 +0900 Subject: [PATCH 19/22] [sqlite] support LIMIT and OFFSET --- internal/engine/sqlite/convert.go | 386 ++++++++++++++++-------------- internal/engine/sqlite/parse.go | 3 +- 2 files changed, 208 insertions(+), 181 deletions(-) diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 1c9fb38923..42af9ca860 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -1,46 +1,66 @@ package sqlite import ( + "log" "strconv" "strings" "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/kyleconroy/sqlc/internal/debug" "github.com/kyleconroy/sqlc/internal/engine/sqlite/parser" "github.com/kyleconroy/sqlc/internal/sql/ast" ) +type cc struct { + paramCount int +} + type node interface { GetParser() antlr.Parser } -func convertAlter_table_stmtContext(c *parser.Alter_table_stmtContext) ast.Node { +func todo(n node) *ast.TODO { + if debug.Active { + log.Printf("sqlite.convert: Unknown node type %T\n", n) + } + return &ast.TODO{} +} + +func identifier(id string) string { + return strings.ToLower(id) +} + +func NewIdentifer(t string) *ast.String { + return &ast.String{Str: identifier(t)} +} - if c.RENAME_() != nil { - if newTable, ok := c.New_table_name().(*parser.New_table_nameContext); ok { +func (c *cc) convertAlter_table_stmtContext(n *parser.Alter_table_stmtContext) ast.Node { + if n.RENAME_() != nil { + if newTable, ok := n.New_table_name().(*parser.New_table_nameContext); ok { name := newTable.Any_name().GetText() return &ast.RenameTableStmt{ - Table: parseTableName(c), + Table: parseTableName(n), NewName: &name, } } - if newCol, ok := c.GetNew_column_name().(*parser.Column_nameContext); ok { + if newCol, ok := n.GetNew_column_name().(*parser.Column_nameContext); ok { name := newCol.Any_name().GetText() return &ast.RenameColumnStmt{ - Table: parseTableName(c), + Table: parseTableName(n), Col: &ast.ColumnRef{ - Name: c.GetOld_column_name().GetText(), + Name: n.GetOld_column_name().GetText(), }, NewName: &name, } } } - if c.ADD_() != nil { - if def, ok := c.Column_def().(*parser.Column_defContext); ok { + if n.ADD_() != nil { + if def, ok := n.Column_def().(*parser.Column_defContext); ok { stmt := &ast.AlterTableStmt{ - Table: parseTableName(c), + Table: parseTableName(n), Cmds: &ast.List{}, } name := def.Column_name().GetText() @@ -59,13 +79,12 @@ func convertAlter_table_stmtContext(c *parser.Alter_table_stmtContext) ast.Node } } - if c.DROP_() != nil { + if n.DROP_() != nil { stmt := &ast.AlterTableStmt{ - Table: parseTableName(c), + Table: parseTableName(n), Cmds: &ast.List{}, } - name := c.Column_name(0).GetText() - //fmt.Printf("column: %s", name) + name := n.Column_name(0).GetText() stmt.Cmds.Items = append(stmt.Cmds.Items, &ast.AlterTableCmd{ Name: &name, Subtype: ast.AT_DropColumn, @@ -73,22 +92,22 @@ func convertAlter_table_stmtContext(c *parser.Alter_table_stmtContext) ast.Node return stmt } - return &ast.TODO{} + return todo(n) } -func convertAttach_stmtContext(c *parser.Attach_stmtContext) ast.Node { - name := c.Schema_name().GetText() +func (c *cc) convertAttach_stmtContext(n *parser.Attach_stmtContext) ast.Node { + name := n.Schema_name().GetText() return &ast.CreateSchemaStmt{ Name: &name, } } -func convertCreate_table_stmtContext(c *parser.Create_table_stmtContext) ast.Node { +func (c *cc) convertCreate_table_stmtContext(n *parser.Create_table_stmtContext) ast.Node { stmt := &ast.CreateTableStmt{ - Name: parseTableName(c), - IfNotExists: c.EXISTS_() != nil, + Name: parseTableName(n), + IfNotExists: n.EXISTS_() != nil, } - for _, idef := range c.AllColumn_def() { + for _, idef := range n.AllColumn_def() { if def, ok := idef.(*parser.Column_defContext); ok { stmt.Cols = append(stmt.Cols, &ast.ColumnDef{ Colname: identifier(def.Column_name().GetText()), @@ -100,29 +119,29 @@ func convertCreate_table_stmtContext(c *parser.Create_table_stmtContext) ast.Nod return stmt } -func convertCreate_view_stmtContext(c *parser.Create_view_stmtContext) ast.Node { - viewName := c.View_name().GetText() +func (c *cc) convertCreate_view_stmtContext(n *parser.Create_view_stmtContext) ast.Node { + viewName := n.View_name().GetText() relation := &ast.RangeVar{ Relname: &viewName, } - if c.Schema_name() != nil { - schemaName := c.Schema_name().GetText() + if n.Schema_name() != nil { + schemaName := n.Schema_name().GetText() relation.Schemaname = &schemaName } return &ast.ViewStmt{ View: relation, Aliases: &ast.List{}, - Query: convert(c.Select_stmt()), + Query: c.convert(n.Select_stmt()), Replace: false, Options: &ast.List{}, WithCheckOption: ast.ViewCheckOption(0), } } -func convertDelete_stmtContext(c *parser.Delete_stmtContext) ast.Node { - if qualifiedName, ok := c.Qualified_table_name().(*parser.Qualified_table_nameContext); ok { +func (c *cc) convertDelete_stmtContext(n *parser.Delete_stmtContext) ast.Node { + if qualifiedName, ok := n.Qualified_table_name().(*parser.Qualified_table_nameContext); ok { tableName := qualifiedName.Table_name().GetText() relation := &ast.RangeVar{ @@ -145,51 +164,40 @@ func convertDelete_stmtContext(c *parser.Delete_stmtContext) ast.Node { WithClause: nil, } - if c.WHERE_() != nil { - if c.Expr() != nil { - delete.WhereClause = convert(c.Expr()) - } + if n.WHERE_() != nil && n.Expr() != nil { + delete.WhereClause = c.convert(n.Expr()) } return delete } - return &ast.TODO{} + return todo(n) } -func convertDrop_stmtContext(c *parser.Drop_stmtContext) ast.Node { - if c.TABLE_() != nil || c.VIEW_() != nil { +func (c *cc) convertDrop_stmtContext(n *parser.Drop_stmtContext) ast.Node { + if n.TABLE_() != nil || n.VIEW_() != nil { name := ast.TableName{ - Name: c.Any_name().GetText(), + Name: n.Any_name().GetText(), } - if c.Schema_name() != nil { - name.Schema = c.Schema_name().GetText() + if n.Schema_name() != nil { + name.Schema = n.Schema_name().GetText() } return &ast.DropTableStmt{ - IfExists: c.EXISTS_() != nil, + IfExists: n.EXISTS_() != nil, Tables: []*ast.TableName{&name}, } - } else { - return &ast.TODO{} } + return todo(n) } -func identifier(id string) string { - return strings.ToLower(id) -} - -func NewIdentifer(t string) *ast.String { - return &ast.String{Str: identifier(t)} -} - -func convertFuncContext(c *parser.Expr_functionContext) ast.Node { - if name, ok := c.Function_name().(*parser.Function_nameContext); ok { +func (c *cc) convertFuncContext(n *parser.Expr_functionContext) ast.Node { + if name, ok := n.Function_name().(*parser.Function_nameContext); ok { funcName := strings.ToLower(name.GetText()) var argNodes []ast.Node - for _, exp := range c.AllExpr() { - argNodes = append(argNodes, convert(exp)) + for _, exp := range n.AllExpr() { + argNodes = append(argNodes, c.convert(exp)) } args := &ast.List{Items: argNodes} @@ -207,36 +215,36 @@ func convertFuncContext(c *parser.Expr_functionContext) ast.Node { NewIdentifer(funcName), }, }, - AggStar: c.STAR() != nil, + AggStar: n.STAR() != nil, Args: args, AggOrder: &ast.List{}, - AggDistinct: c.DISTINCT_() != nil, + AggDistinct: n.DISTINCT_() != nil, } } } - return &ast.TODO{} + return todo(n) } -func convertExprContext(c *parser.ExprContext) ast.Node { - return &ast.TODO{} +func (c *cc) convertExprContext(n *parser.ExprContext) ast.Node { + return &ast.Expr{} } -func convertColumnNameExpr(c *parser.Expr_qualified_column_nameContext) *ast.ColumnRef { +func (c *cc) convertColumnNameExpr(n *parser.Expr_qualified_column_nameContext) *ast.ColumnRef { var items []ast.Node - if schema, ok := c.Schema_name().(*parser.Schema_nameContext); ok { + if schema, ok := n.Schema_name().(*parser.Schema_nameContext); ok { schemaText := schema.GetText() if schemaText != "" { items = append(items, NewIdentifer(schemaText)) } } - if table, ok := c.Table_name().(*parser.Table_nameContext); ok { + if table, ok := n.Table_name().(*parser.Table_nameContext); ok { tableName := table.GetText() if tableName != "" { items = append(items, NewIdentifer(tableName)) } } - items = append(items, NewIdentifer(c.Column_name().GetText())) + items = append(items, NewIdentifer(n.Column_name().GetText())) return &ast.ColumnRef{ Fields: &ast.List{ Items: items, @@ -244,90 +252,90 @@ func convertColumnNameExpr(c *parser.Expr_qualified_column_nameContext) *ast.Col } } -func convertComparison(c *parser.Expr_comparisonContext) ast.Node { +func (c *cc) convertComparison(n *parser.Expr_comparisonContext) ast.Node { aExpr := &ast.A_Expr{ Name: &ast.List{ Items: []ast.Node{ &ast.String{Str: "="}, // TODO: add actual comparison }, }, - Lexpr: convert(c.Expr(0)), - Rexpr: convert(c.Expr(1)), + Lexpr: c.convert(n.Expr(0)), + Rexpr: c.convert(n.Expr(1)), } return aExpr } -func convertMultiSelect_stmtContext(c *parser.Select_stmtContext) ast.Node { +func (c *cc) convertMultiSelect_stmtContext(n *parser.Select_stmtContext) ast.Node { var tables []ast.Node var cols []ast.Node var where ast.Node - var groupBy = &ast.List{Items: []ast.Node{}} + var groups = []ast.Node{} var having ast.Node - for _, icore := range c.AllSelect_core() { + for _, icore := range n.AllSelect_core() { core, ok := icore.(*parser.Select_coreContext) if !ok { continue } - cols = append(cols, getCols(core)...) - tables = append(tables, getTables(core)...) + cols = append(cols, c.getCols(core)...) + tables = append(tables, c.getTables(core)...) i := 0 if core.WHERE_() != nil { - where = convert(core.Expr(i)) + where = c.convert(core.Expr(i)) i++ } if core.GROUP_() != nil { - n := len(core.AllExpr()) - i + l := len(core.AllExpr()) - i if core.HAVING_() != nil { - n-- + having = c.convert(core.Expr(l)) + l-- } - for i < n { - groupBy.Items = append(groupBy.Items, convert(core.Expr(i))) - i++ - } - - if core.HAVING_() != nil { - having = convert(core.Expr(i)) + for i < l { + groups = append(groups, c.convert(core.Expr(i))) i++ } } } window := &ast.List{Items: []ast.Node{}} - if c.Order_by_stmt() != nil { - window.Items = append(window.Items, convert(c.Order_by_stmt())) + if n.Order_by_stmt() != nil { + window.Items = append(window.Items, c.convert(n.Order_by_stmt())) } + limitCount, limitOffset := c.convertLimit_stmtContext(n.Limit_stmt()) + return &ast.SelectStmt{ FromClause: &ast.List{Items: tables}, TargetList: &ast.List{Items: cols}, WhereClause: where, - GroupClause: groupBy, + GroupClause: &ast.List{Items: groups}, HavingClause: having, WindowClause: window, + LimitCount: limitCount, + LimitOffset: limitOffset, ValuesLists: &ast.List{}, } } -func getTables(core *parser.Select_coreContext) []ast.Node { +func (c *cc) getTables(core *parser.Select_coreContext) []ast.Node { var tables []ast.Node - tables = append(tables, convertTablesOrSubquery(core.AllTable_or_subquery())...) + tables = append(tables, c.convertTablesOrSubquery(core.AllTable_or_subquery())...) if core.Join_clause() != nil { join, ok := core.Join_clause().(*parser.Join_clauseContext) if ok { - tables = append(tables, convertTablesOrSubquery(join.AllTable_or_subquery())...) + tables = append(tables, c.convertTablesOrSubquery(join.AllTable_or_subquery())...) } } return tables } -func getCols(core *parser.Select_coreContext) []ast.Node { +func (c *cc) getCols(core *parser.Select_coreContext) []ast.Node { var cols []ast.Node for _, icol := range core.AllResult_column() { col, ok := icol.(*parser.Result_columnContext) @@ -341,9 +349,9 @@ func getCols(core *parser.Select_coreContext) []ast.Node { iexpr := col.Expr() switch { case col.STAR() != nil: - val = convertWildCardField(col) + val = c.convertWildCardField(col) case iexpr != nil: - val = convert(iexpr) + val = c.convert(iexpr) } if val == nil { @@ -361,10 +369,10 @@ func getCols(core *parser.Select_coreContext) []ast.Node { return cols } -func convertWildCardField(c *parser.Result_columnContext) *ast.ColumnRef { +func (c *cc) convertWildCardField(n *parser.Result_columnContext) *ast.ColumnRef { items := []ast.Node{} - if c.Table_name() != nil { - items = append(items, NewIdentifer(c.Table_name().GetText())) + if n.Table_name() != nil { + items = append(items, NewIdentifer(n.Table_name().GetText())) } items = append(items, &ast.A_Star{}) @@ -372,12 +380,12 @@ func convertWildCardField(c *parser.Result_columnContext) *ast.ColumnRef { Fields: &ast.List{ Items: items, }, - Location: c.GetStart().GetStart(), + Location: n.GetStart().GetStart(), } } -func convertOrderby_stmtContext(c parser.IOrder_by_stmtContext) ast.Node { - if orderBy, ok := c.(*parser.Order_by_stmtContext); ok { +func (c *cc) convertOrderby_stmtContext(n parser.IOrder_by_stmtContext) ast.Node { + if orderBy, ok := n.(*parser.Order_by_stmtContext); ok { list := &ast.List{Items: []ast.Node{}} for _, o := range orderBy.AllOrdering_term() { term, ok := o.(*parser.Ordering_termContext) @@ -385,93 +393,109 @@ func convertOrderby_stmtContext(c parser.IOrder_by_stmtContext) ast.Node { continue } list.Items = append(list.Items, &ast.CaseExpr{ - Xpr: convert(term.Expr()), + Xpr: c.convert(term.Expr()), Location: term.Expr().GetStart().GetStart(), }) } return list } - return &ast.TODO{} + return todo(n) } -func convertSql_stmtContext(n *parser.Sql_stmtContext) ast.Node { +func (c *cc) convertLimit_stmtContext(n parser.ILimit_stmtContext) (ast.Node, ast.Node) { + if n == nil { + return nil, nil + } + + var limitCount, limitOffset ast.Node + if limit, ok := n.(*parser.Limit_stmtContext); ok { + limitCount = c.convert(limit.Expr(0)) + if limit.OFFSET_() != nil { + limitOffset = c.convert(limit.Expr(1)) + } + } + + return limitCount, limitOffset +} + +func (c *cc) convertSql_stmtContext(n *parser.Sql_stmtContext) ast.Node { if stmt := n.Alter_table_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Analyze_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Attach_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Begin_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Commit_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Create_index_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Create_table_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Create_trigger_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Create_view_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Create_virtual_table_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Delete_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Delete_stmt_limited(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Detach_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Drop_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Insert_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Pragma_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Reindex_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Release_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Rollback_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Savepoint_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Select_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Update_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Update_stmt_limited(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } if stmt := n.Vacuum_stmt(); stmt != nil { - return convert(stmt) + return c.convert(stmt) } return nil } -func convertLiteral(c *parser.Expr_literalContext) ast.Node { - if literal, ok := c.Literal_value().(*parser.Literal_valueContext); ok { +func (c *cc) convertLiteral(n *parser.Expr_literalContext) ast.Node { + if literal, ok := n.Literal_value().(*parser.Literal_valueContext); ok { if literal.NUMERIC_LITERAL() != nil { i, _ := strconv.ParseInt(literal.GetText(), 10, 64) @@ -496,55 +520,57 @@ func convertLiteral(c *parser.Expr_literalContext) ast.Node { Val: &ast.Integer{Ival: i}, } } - } - return &ast.TODO{} + return todo(n) } -func convertMathOperationNode(c *parser.Expr_math_opContext) ast.Node { +func (c *cc) convertMathOperationNode(n *parser.Expr_math_opContext) ast.Node { return &ast.A_Expr{ Name: &ast.List{ Items: []ast.Node{ &ast.String{Str: "+"}, // todo: Convert operation types }, }, - Lexpr: convert(c.Expr(0)), - Rexpr: convert(c.Expr(1)), + Lexpr: c.convert(n.Expr(0)), + Rexpr: c.convert(n.Expr(1)), } } -func convertBinaryNode(c *parser.Expr_binaryContext) ast.Node { +func (c *cc) convertBinaryNode(n *parser.Expr_binaryContext) ast.Node { return &ast.BoolExpr{ // TODO: Set op Args: &ast.List{ Items: []ast.Node{ - convert(c.Expr(0)), - convert(c.Expr(1)), + c.convert(n.Expr(0)), + c.convert(n.Expr(1)), }, }, } } -func convertParam(c *parser.Expr_bindContext) ast.Node { - if c.BIND_PARAMETER() != nil { - return &ast.ParamRef{ // TODO: Need to count these up instead of always using 0 - Location: c.GetStart().GetStart(), +func (c *cc) convertParam(n *parser.Expr_bindContext) ast.Node { + if n.BIND_PARAMETER() != nil { + // Parameter numbers start at one + c.paramCount += 1 + return &ast.ParamRef{ + Number: c.paramCount, + Location: n.GetStart().GetStart(), } } - return &ast.TODO{} + return todo(n) } -func convertInsert_stmtContext(c *parser.Insert_stmtContext) ast.Node { - tableName := c.Table_name().GetText() +func (c *cc) convertInsert_stmtContext(n *parser.Insert_stmtContext) ast.Node { + tableName := n.Table_name().GetText() rel := &ast.RangeVar{ Relname: &tableName, } - if c.Schema_name() != nil { - schemaName := c.Schema_name().GetText() + if n.Schema_name() != nil { + schemaName := n.Schema_name().GetText() rel.Schemaname = &schemaName } - if c.Table_alias() != nil { - tableAlias := c.Table_alias().GetText() + if n.Table_alias() != nil { + tableAlias := n.Table_alias().GetText() rel.Alias = &ast.Alias{ Aliasname: &tableAlias, } @@ -552,36 +578,36 @@ func convertInsert_stmtContext(c *parser.Insert_stmtContext) ast.Node { insert := &ast.InsertStmt{ Relation: rel, - Cols: convertColumnNames(c.AllColumn_name()), + Cols: c.convertColumnNames(n.AllColumn_name()), ReturningList: &ast.List{}, } - if ss, ok := convert(c.Select_stmt()).(*ast.SelectStmt); ok { + if ss, ok := c.convert(n.Select_stmt()).(*ast.SelectStmt); ok { ss.ValuesLists = &ast.List{} insert.SelectStmt = ss } else { insert.SelectStmt = &ast.SelectStmt{ FromClause: &ast.List{}, TargetList: &ast.List{}, - ValuesLists: convertExprLists(c.AllExpr()), + ValuesLists: c.convertExprLists(n.AllExpr()), } } return insert } -func convertExprLists(lists []parser.IExprContext) *ast.List { +func (c *cc) convertExprLists(lists []parser.IExprContext) *ast.List { list := &ast.List{Items: []ast.Node{}} n := len(lists) inner := &ast.List{Items: []ast.Node{}} for i := 0; i < n; i++ { - inner.Items = append(inner.Items, convert(lists[i])) + inner.Items = append(inner.Items, c.convert(lists[i])) } list.Items = append(list.Items, inner) return list } -func convertColumnNames(cols []parser.IColumn_nameContext) *ast.List { +func (c *cc) convertColumnNames(cols []parser.IColumn_nameContext) *ast.List { list := &ast.List{Items: []ast.Node{}} for _, c := range cols { name := c.GetText() @@ -592,7 +618,7 @@ func convertColumnNames(cols []parser.IColumn_nameContext) *ast.List { return list } -func convertTablesOrSubquery(tableOrSubquery []parser.ITable_or_subqueryContext) []ast.Node { +func (c *cc) convertTablesOrSubquery(tableOrSubquery []parser.ITable_or_subqueryContext) []ast.Node { var tables []ast.Node for _, ifrom := range tableOrSubquery { from, ok := ifrom.(*parser.Table_or_subqueryContext) @@ -619,32 +645,32 @@ func convertTablesOrSubquery(tableOrSubquery []parser.ITable_or_subqueryContext) return tables } -func convertUpdate_stmtContext(c *parser.Update_stmtContext) ast.Node { - if c == nil { +func (c *cc) convertUpdate_stmtContext(n *parser.Update_stmtContext) ast.Node { + if n == nil { return nil } relations := &ast.List{} - tableName := c.Qualified_table_name().GetText() + tableName := n.Qualified_table_name().GetText() rel := ast.RangeVar{ Relname: &tableName, - Location: c.GetStart().GetStart(), + Location: n.GetStart().GetStart(), } relations.Items = append(relations.Items, &rel) list := &ast.List{} - for i, col := range c.AllColumn_name() { + for i, col := range n.AllColumn_name() { colName := col.GetText() target := &ast.ResTarget{ Name: &colName, - Val: convert(c.Expr(i)), + Val: c.convert(n.Expr(i)), } list.Items = append(list.Items, target) } var where ast.Node = nil - if c.WHERE_() != nil { - where = convert(c.Expr(len(c.AllExpr()) - 1)) + if n.WHERE_() != nil { + where = c.convert(n.Expr(len(n.AllExpr()) - 1)) } return &ast.UpdateStmt{ @@ -657,71 +683,71 @@ func convertUpdate_stmtContext(c *parser.Update_stmtContext) ast.Node { } } -func convert(node node) ast.Node { +func (c *cc) convert(node node) ast.Node { switch n := node.(type) { case *parser.Alter_table_stmtContext: - return convertAlter_table_stmtContext(n) + return c.convertAlter_table_stmtContext(n) case *parser.Attach_stmtContext: - return convertAttach_stmtContext(n) + return c.convertAttach_stmtContext(n) case *parser.Create_table_stmtContext: - return convertCreate_table_stmtContext(n) + return c.convertCreate_table_stmtContext(n) case *parser.Create_view_stmtContext: - return convertCreate_view_stmtContext(n) + return c.convertCreate_view_stmtContext(n) case *parser.Drop_stmtContext: - return convertDrop_stmtContext(n) + return c.convertDrop_stmtContext(n) case *parser.Delete_stmtContext: - return convertDelete_stmtContext(n) + return c.convertDelete_stmtContext(n) case *parser.ExprContext: - return convertExprContext(n) + return c.convertExprContext(n) case *parser.Expr_functionContext: - return convertFuncContext(n) + return c.convertFuncContext(n) case *parser.Expr_qualified_column_nameContext: - return convertColumnNameExpr(n) + return c.convertColumnNameExpr(n) case *parser.Expr_comparisonContext: - return convertComparison(n) + return c.convertComparison(n) case *parser.Expr_bindContext: - return convertParam(n) + return c.convertParam(n) case *parser.Expr_literalContext: - return convertLiteral(n) + return c.convertLiteral(n) case *parser.Expr_binaryContext: - return convertBinaryNode(n) + return c.convertBinaryNode(n) case *parser.Expr_math_opContext: - return convertMathOperationNode(n) + return c.convertMathOperationNode(n) case *parser.Factored_select_stmtContext: // TODO: need to handle this - return &ast.TODO{} + return todo(n) case *parser.Insert_stmtContext: - return convertInsert_stmtContext(n) + return c.convertInsert_stmtContext(n) case *parser.Order_by_stmtContext: - return convertOrderby_stmtContext(n) + return c.convertOrderby_stmtContext(n) case *parser.Select_stmtContext: - return convertMultiSelect_stmtContext(n) + return c.convertMultiSelect_stmtContext(n) case *parser.Sql_stmtContext: - return convertSql_stmtContext(n) + return c.convertSql_stmtContext(n) case *parser.Update_stmtContext: - return convertUpdate_stmtContext(n) + return c.convertUpdate_stmtContext(n) default: - return &ast.TODO{} + return todo(n) } } diff --git a/internal/engine/sqlite/parse.go b/internal/engine/sqlite/parse.go index b48d194b68..51e1ffb25c 100644 --- a/internal/engine/sqlite/parse.go +++ b/internal/engine/sqlite/parse.go @@ -67,7 +67,8 @@ func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) { loc := 0 for _, stmt := range list.AllSql_stmt() { - out := convert(stmt) + converter := &cc{} + out := converter.convert(stmt) if _, ok := out.(*ast.TODO); ok { continue } From 0844e6562653d1afbe9744449049b5958bd5ef48 Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Thu, 16 Jun 2022 23:41:03 +0900 Subject: [PATCH 20/22] add endtoend test for SQLite * limit * limit_offset --- .../endtoend/testdata/limit/sqlite/go/db.go | 31 ++++++++++++++ .../testdata/limit/sqlite/go/models.go | 11 +++++ .../testdata/limit/sqlite/go/query.sql.go | 37 ++++++++++++++++ .../endtoend/testdata/limit/sqlite/query.sql | 4 ++ .../endtoend/testdata/limit/sqlite/sqlc.json | 12 ++++++ .../testdata/limit_offset/pgx/go/db.go | 32 ++++++++++++++ .../testdata/limit_offset/pgx/go/models.go | 11 +++++ .../testdata/limit_offset/pgx/go/query.sql.go | 39 +++++++++++++++++ .../testdata/limit_offset/pgx/query.sql | 4 ++ .../testdata/limit_offset/pgx/sqlc.json | 13 ++++++ .../testdata/limit_offset/sqlite/go/db.go | 31 ++++++++++++++ .../testdata/limit_offset/sqlite/go/models.go | 11 +++++ .../limit_offset/sqlite/go/query.sql.go | 42 +++++++++++++++++++ .../testdata/limit_offset/sqlite/query.sql | 4 ++ .../testdata/limit_offset/sqlite/sqlc.json | 12 ++++++ .../testdata/limit_offset/stdlib/go/db.go | 31 ++++++++++++++ .../testdata/limit_offset/stdlib/go/models.go | 11 +++++ .../limit_offset/stdlib/go/query.sql.go | 42 +++++++++++++++++++ .../testdata/limit_offset/stdlib/query.sql | 4 ++ .../testdata/limit_offset/stdlib/sqlc.json | 11 +++++ 20 files changed, 393 insertions(+) create mode 100644 internal/endtoend/testdata/limit/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/limit/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/limit/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/limit/sqlite/query.sql create mode 100644 internal/endtoend/testdata/limit/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/limit_offset/pgx/go/db.go create mode 100644 internal/endtoend/testdata/limit_offset/pgx/go/models.go create mode 100644 internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go create mode 100644 internal/endtoend/testdata/limit_offset/pgx/query.sql create mode 100644 internal/endtoend/testdata/limit_offset/pgx/sqlc.json create mode 100644 internal/endtoend/testdata/limit_offset/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/limit_offset/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/limit_offset/sqlite/query.sql create mode 100644 internal/endtoend/testdata/limit_offset/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/limit_offset/stdlib/go/db.go create mode 100644 internal/endtoend/testdata/limit_offset/stdlib/go/models.go create mode 100644 internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go create mode 100644 internal/endtoend/testdata/limit_offset/stdlib/query.sql create mode 100644 internal/endtoend/testdata/limit_offset/stdlib/sqlc.json diff --git a/internal/endtoend/testdata/limit/sqlite/go/db.go b/internal/endtoend/testdata/limit/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/limit/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/limit/sqlite/go/models.go b/internal/endtoend/testdata/limit/sqlite/go/models.go new file mode 100644 index 0000000000..0a2313d91e --- /dev/null +++ b/internal/endtoend/testdata/limit/sqlite/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Foo struct { + Bar bool +} diff --git a/internal/endtoend/testdata/limit/sqlite/go/query.sql.go b/internal/endtoend/testdata/limit/sqlite/go/query.sql.go new file mode 100644 index 0000000000..ac2d95ddad --- /dev/null +++ b/internal/endtoend/testdata/limit/sqlite/go/query.sql.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const limitMe = `-- name: LimitMe :many +SELECT bar FROM foo LIMIT ? +` + +func (q *Queries) LimitMe(ctx context.Context, limit int64) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, limitMe, limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var bar bool + if err := rows.Scan(&bar); err != nil { + return nil, err + } + items = append(items, bar) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/limit/sqlite/query.sql b/internal/endtoend/testdata/limit/sqlite/query.sql new file mode 100644 index 0000000000..e7e373d13b --- /dev/null +++ b/internal/endtoend/testdata/limit/sqlite/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (bar bool not null); + +-- name: LimitMe :many +SELECT bar FROM foo LIMIT ?; diff --git a/internal/endtoend/testdata/limit/sqlite/sqlc.json b/internal/endtoend/testdata/limit/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/limit/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/limit_offset/pgx/go/db.go b/internal/endtoend/testdata/limit_offset/pgx/go/db.go new file mode 100644 index 0000000000..6c6a7eb822 --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/pgx/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/limit_offset/pgx/go/models.go b/internal/endtoend/testdata/limit_offset/pgx/go/models.go new file mode 100644 index 0000000000..0a2313d91e --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/pgx/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Foo struct { + Bar bool +} diff --git a/internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go b/internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go new file mode 100644 index 0000000000..2005de3ca1 --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go @@ -0,0 +1,39 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const limitMe = `-- name: LimitMe :many +SELECT bar FROM foo LIMIT $1 OFFSET $2 +` + +type LimitMeParams struct { + Limit int32 + Offset int32 +} + +func (q *Queries) LimitMe(ctx context.Context, arg LimitMeParams) ([]bool, error) { + rows, err := q.db.Query(ctx, limitMe, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var bar bool + if err := rows.Scan(&bar); err != nil { + return nil, err + } + items = append(items, bar) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/limit_offset/pgx/query.sql b/internal/endtoend/testdata/limit_offset/pgx/query.sql new file mode 100644 index 0000000000..efcff8467f --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/pgx/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (bar bool not null); + +-- name: LimitMe :many +SELECT bar FROM foo LIMIT $1 OFFSET $2; diff --git a/internal/endtoend/testdata/limit_offset/pgx/sqlc.json b/internal/endtoend/testdata/limit_offset/pgx/sqlc.json new file mode 100644 index 0000000000..9403bd0279 --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/pgx/sqlc.json @@ -0,0 +1,13 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v4", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/limit_offset/sqlite/go/db.go b/internal/endtoend/testdata/limit_offset/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/limit_offset/sqlite/go/models.go b/internal/endtoend/testdata/limit_offset/sqlite/go/models.go new file mode 100644 index 0000000000..0a2313d91e --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/sqlite/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Foo struct { + Bar bool +} diff --git a/internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go b/internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go new file mode 100644 index 0000000000..436484c545 --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go @@ -0,0 +1,42 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const limitMe = `-- name: LimitMe :many +SELECT bar FROM foo LIMIT ? OFFSET ? +` + +type LimitMeParams struct { + Limit int64 + Offset int64 +} + +func (q *Queries) LimitMe(ctx context.Context, arg LimitMeParams) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, limitMe, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var bar bool + if err := rows.Scan(&bar); err != nil { + return nil, err + } + items = append(items, bar) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/limit_offset/sqlite/query.sql b/internal/endtoend/testdata/limit_offset/sqlite/query.sql new file mode 100644 index 0000000000..b2f254ff26 --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/sqlite/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (bar bool not null); + +-- name: LimitMe :many +SELECT bar FROM foo LIMIT ? OFFSET ?; diff --git a/internal/endtoend/testdata/limit_offset/sqlite/sqlc.json b/internal/endtoend/testdata/limit_offset/sqlite/sqlc.json new file mode 100644 index 0000000000..2ef8a6a81d --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "_lemon", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/limit_offset/stdlib/go/db.go b/internal/endtoend/testdata/limit_offset/stdlib/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/stdlib/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/limit_offset/stdlib/go/models.go b/internal/endtoend/testdata/limit_offset/stdlib/go/models.go new file mode 100644 index 0000000000..0a2313d91e --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/stdlib/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import () + +type Foo struct { + Bar bool +} diff --git a/internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go b/internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go new file mode 100644 index 0000000000..9cb5586eb3 --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go @@ -0,0 +1,42 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const limitMe = `-- name: LimitMe :many +SELECT bar FROM foo LIMIT $1 OFFSET $2 +` + +type LimitMeParams struct { + Limit int32 + Offset int32 +} + +func (q *Queries) LimitMe(ctx context.Context, arg LimitMeParams) ([]bool, error) { + rows, err := q.db.QueryContext(ctx, limitMe, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []bool + for rows.Next() { + var bar bool + if err := rows.Scan(&bar); err != nil { + return nil, err + } + items = append(items, bar) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/limit_offset/stdlib/query.sql b/internal/endtoend/testdata/limit_offset/stdlib/query.sql new file mode 100644 index 0000000000..efcff8467f --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/stdlib/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (bar bool not null); + +-- name: LimitMe :many +SELECT bar FROM foo LIMIT $1 OFFSET $2; diff --git a/internal/endtoend/testdata/limit_offset/stdlib/sqlc.json b/internal/endtoend/testdata/limit_offset/stdlib/sqlc.json new file mode 100644 index 0000000000..ac7c2ed829 --- /dev/null +++ b/internal/endtoend/testdata/limit_offset/stdlib/sqlc.json @@ -0,0 +1,11 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} From e9d3a1f7775d7de84e8d5ac64f26af0b17d18cea Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Sat, 18 Jun 2022 03:40:23 +0900 Subject: [PATCH 21/22] add endtoend test for SQLite * select_limit * limit_offset * single_param_conflict * update_set * update_set_multiple --- .../testdata/limit_offset/pgx/go/db.go | 32 --------- .../testdata/limit_offset/pgx/go/query.sql.go | 39 ---------- .../testdata/limit_offset/pgx/query.sql | 4 -- .../limit_offset/sqlite/go/query.sql.go | 42 ----------- .../testdata/limit_offset/sqlite/query.sql | 4 -- .../limit_offset/stdlib/go/query.sql.go | 42 ----------- .../testdata/limit_offset/stdlib/query.sql | 4 -- .../sqlite/go/db.go | 0 .../sqlite}/go/models.go | 6 +- .../select_limit/sqlite/go/query.sql.go | 72 +++++++++++++++++++ .../testdata/select_limit/sqlite/query.sql | 9 +++ .../stdlib => select_limit/sqlite}/sqlc.json | 3 +- .../sqlite}/go/db.go | 0 .../single_param_conflict/sqlite/go/models.go | 19 +++++ .../sqlite/go/query.sql.go | 50 +++++++++++++ .../single_param_conflict/sqlite/query.sql | 29 ++++++++ .../sqlite}/sqlc.json | 5 +- .../testdata/update_set/sqlite/go/db.go | 31 ++++++++ .../pgx => update_set/sqlite}/go/models.go | 3 +- .../update_set/sqlite/go/query.sql.go | 24 +++++++ .../testdata/update_set/sqlite/query.sql | 4 ++ .../testdata/update_set/sqlite/sqlc.json | 12 ++++ .../update_set_multiple/sqlite/go/db.go | 31 ++++++++ .../sqlite/go/models.go | 3 +- .../sqlite/go/query.sql.go | 24 +++++++ .../update_set_multiple/sqlite/query.sql | 4 ++ .../sqlite/sqlc.json | 0 27 files changed, 321 insertions(+), 175 deletions(-) delete mode 100644 internal/endtoend/testdata/limit_offset/pgx/go/db.go delete mode 100644 internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go delete mode 100644 internal/endtoend/testdata/limit_offset/pgx/query.sql delete mode 100644 internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go delete mode 100644 internal/endtoend/testdata/limit_offset/sqlite/query.sql delete mode 100644 internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go delete mode 100644 internal/endtoend/testdata/limit_offset/stdlib/query.sql rename internal/endtoend/testdata/{limit_offset => select_limit}/sqlite/go/db.go (100%) rename internal/endtoend/testdata/{limit_offset/stdlib => select_limit/sqlite}/go/models.go (71%) create mode 100644 internal/endtoend/testdata/select_limit/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/select_limit/sqlite/query.sql rename internal/endtoend/testdata/{limit_offset/stdlib => select_limit/sqlite}/sqlc.json (85%) rename internal/endtoend/testdata/{limit_offset/stdlib => single_param_conflict/sqlite}/go/db.go (100%) create mode 100644 internal/endtoend/testdata/single_param_conflict/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/single_param_conflict/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/single_param_conflict/sqlite/query.sql rename internal/endtoend/testdata/{limit_offset/pgx => single_param_conflict/sqlite}/sqlc.json (71%) create mode 100644 internal/endtoend/testdata/update_set/sqlite/go/db.go rename internal/endtoend/testdata/{limit_offset/pgx => update_set/sqlite}/go/models.go (82%) create mode 100644 internal/endtoend/testdata/update_set/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/update_set/sqlite/query.sql create mode 100644 internal/endtoend/testdata/update_set/sqlite/sqlc.json create mode 100644 internal/endtoend/testdata/update_set_multiple/sqlite/go/db.go rename internal/endtoend/testdata/{limit_offset => update_set_multiple}/sqlite/go/models.go (82%) create mode 100644 internal/endtoend/testdata/update_set_multiple/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/update_set_multiple/sqlite/query.sql rename internal/endtoend/testdata/{limit_offset => update_set_multiple}/sqlite/sqlc.json (100%) diff --git a/internal/endtoend/testdata/limit_offset/pgx/go/db.go b/internal/endtoend/testdata/limit_offset/pgx/go/db.go deleted file mode 100644 index 6c6a7eb822..0000000000 --- a/internal/endtoend/testdata/limit_offset/pgx/go/db.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.14.0 - -package querytest - -import ( - "context" - - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" -) - -type DBTX interface { - Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) - Query(context.Context, string, ...interface{}) (pgx.Rows, error) - QueryRow(context.Context, string, ...interface{}) pgx.Row -} - -func New(db DBTX) *Queries { - return &Queries{db: db} -} - -type Queries struct { - db DBTX -} - -func (q *Queries) WithTx(tx pgx.Tx) *Queries { - return &Queries{ - db: tx, - } -} diff --git a/internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go b/internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go deleted file mode 100644 index 2005de3ca1..0000000000 --- a/internal/endtoend/testdata/limit_offset/pgx/go/query.sql.go +++ /dev/null @@ -1,39 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.14.0 -// source: query.sql - -package querytest - -import ( - "context" -) - -const limitMe = `-- name: LimitMe :many -SELECT bar FROM foo LIMIT $1 OFFSET $2 -` - -type LimitMeParams struct { - Limit int32 - Offset int32 -} - -func (q *Queries) LimitMe(ctx context.Context, arg LimitMeParams) ([]bool, error) { - rows, err := q.db.Query(ctx, limitMe, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []bool - for rows.Next() { - var bar bool - if err := rows.Scan(&bar); err != nil { - return nil, err - } - items = append(items, bar) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/internal/endtoend/testdata/limit_offset/pgx/query.sql b/internal/endtoend/testdata/limit_offset/pgx/query.sql deleted file mode 100644 index efcff8467f..0000000000 --- a/internal/endtoend/testdata/limit_offset/pgx/query.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE foo (bar bool not null); - --- name: LimitMe :many -SELECT bar FROM foo LIMIT $1 OFFSET $2; diff --git a/internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go b/internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go deleted file mode 100644 index 436484c545..0000000000 --- a/internal/endtoend/testdata/limit_offset/sqlite/go/query.sql.go +++ /dev/null @@ -1,42 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.14.0 -// source: query.sql - -package querytest - -import ( - "context" -) - -const limitMe = `-- name: LimitMe :many -SELECT bar FROM foo LIMIT ? OFFSET ? -` - -type LimitMeParams struct { - Limit int64 - Offset int64 -} - -func (q *Queries) LimitMe(ctx context.Context, arg LimitMeParams) ([]bool, error) { - rows, err := q.db.QueryContext(ctx, limitMe, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []bool - for rows.Next() { - var bar bool - if err := rows.Scan(&bar); err != nil { - return nil, err - } - items = append(items, bar) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/internal/endtoend/testdata/limit_offset/sqlite/query.sql b/internal/endtoend/testdata/limit_offset/sqlite/query.sql deleted file mode 100644 index b2f254ff26..0000000000 --- a/internal/endtoend/testdata/limit_offset/sqlite/query.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE foo (bar bool not null); - --- name: LimitMe :many -SELECT bar FROM foo LIMIT ? OFFSET ?; diff --git a/internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go b/internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go deleted file mode 100644 index 9cb5586eb3..0000000000 --- a/internal/endtoend/testdata/limit_offset/stdlib/go/query.sql.go +++ /dev/null @@ -1,42 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.14.0 -// source: query.sql - -package querytest - -import ( - "context" -) - -const limitMe = `-- name: LimitMe :many -SELECT bar FROM foo LIMIT $1 OFFSET $2 -` - -type LimitMeParams struct { - Limit int32 - Offset int32 -} - -func (q *Queries) LimitMe(ctx context.Context, arg LimitMeParams) ([]bool, error) { - rows, err := q.db.QueryContext(ctx, limitMe, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []bool - for rows.Next() { - var bar bool - if err := rows.Scan(&bar); err != nil { - return nil, err - } - items = append(items, bar) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/internal/endtoend/testdata/limit_offset/stdlib/query.sql b/internal/endtoend/testdata/limit_offset/stdlib/query.sql deleted file mode 100644 index efcff8467f..0000000000 --- a/internal/endtoend/testdata/limit_offset/stdlib/query.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE foo (bar bool not null); - --- name: LimitMe :many -SELECT bar FROM foo LIMIT $1 OFFSET $2; diff --git a/internal/endtoend/testdata/limit_offset/sqlite/go/db.go b/internal/endtoend/testdata/select_limit/sqlite/go/db.go similarity index 100% rename from internal/endtoend/testdata/limit_offset/sqlite/go/db.go rename to internal/endtoend/testdata/select_limit/sqlite/go/db.go diff --git a/internal/endtoend/testdata/limit_offset/stdlib/go/models.go b/internal/endtoend/testdata/select_limit/sqlite/go/models.go similarity index 71% rename from internal/endtoend/testdata/limit_offset/stdlib/go/models.go rename to internal/endtoend/testdata/select_limit/sqlite/go/models.go index 0a2313d91e..1628a7e8fe 100644 --- a/internal/endtoend/testdata/limit_offset/stdlib/go/models.go +++ b/internal/endtoend/testdata/select_limit/sqlite/go/models.go @@ -4,8 +4,10 @@ package querytest -import () +import ( + "database/sql" +) type Foo struct { - Bar bool + A sql.NullString } diff --git a/internal/endtoend/testdata/select_limit/sqlite/go/query.sql.go b/internal/endtoend/testdata/select_limit/sqlite/go/query.sql.go new file mode 100644 index 0000000000..98252e140b --- /dev/null +++ b/internal/endtoend/testdata/select_limit/sqlite/go/query.sql.go @@ -0,0 +1,72 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const fooLimit = `-- name: FooLimit :many +SELECT a FROM foo +LIMIT ? +` + +func (q *Queries) FooLimit(ctx context.Context, limit int64) ([]sql.NullString, error) { + rows, err := q.db.QueryContext(ctx, fooLimit, limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []sql.NullString + for rows.Next() { + var a sql.NullString + if err := rows.Scan(&a); err != nil { + return nil, err + } + items = append(items, a) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const fooLimitOffset = `-- name: FooLimitOffset :many +SELECT a FROM foo +LIMIT ? OFFSET ? +` + +type FooLimitOffsetParams struct { + Limit int64 + Offset int64 +} + +func (q *Queries) FooLimitOffset(ctx context.Context, arg FooLimitOffsetParams) ([]sql.NullString, error) { + rows, err := q.db.QueryContext(ctx, fooLimitOffset, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []sql.NullString + for rows.Next() { + var a sql.NullString + if err := rows.Scan(&a); err != nil { + return nil, err + } + items = append(items, a) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/select_limit/sqlite/query.sql b/internal/endtoend/testdata/select_limit/sqlite/query.sql new file mode 100644 index 0000000000..4b5e81997f --- /dev/null +++ b/internal/endtoend/testdata/select_limit/sqlite/query.sql @@ -0,0 +1,9 @@ +CREATE TABLE foo (a text); + +/* name: FooLimit :many */ +SELECT a FROM foo +LIMIT ?; + +/* name: FooLimitOffset :many */ +SELECT a FROM foo +LIMIT ? OFFSET ?; diff --git a/internal/endtoend/testdata/limit_offset/stdlib/sqlc.json b/internal/endtoend/testdata/select_limit/sqlite/sqlc.json similarity index 85% rename from internal/endtoend/testdata/limit_offset/stdlib/sqlc.json rename to internal/endtoend/testdata/select_limit/sqlite/sqlc.json index ac7c2ed829..9aea94d599 100644 --- a/internal/endtoend/testdata/limit_offset/stdlib/sqlc.json +++ b/internal/endtoend/testdata/select_limit/sqlite/sqlc.json @@ -2,10 +2,11 @@ "version": "1", "packages": [ { + "engine": "_lemon", "path": "go", "name": "querytest", "schema": "query.sql", "queries": "query.sql" } ] -} +} \ No newline at end of file diff --git a/internal/endtoend/testdata/limit_offset/stdlib/go/db.go b/internal/endtoend/testdata/single_param_conflict/sqlite/go/db.go similarity index 100% rename from internal/endtoend/testdata/limit_offset/stdlib/go/db.go rename to internal/endtoend/testdata/single_param_conflict/sqlite/go/db.go diff --git a/internal/endtoend/testdata/single_param_conflict/sqlite/go/models.go b/internal/endtoend/testdata/single_param_conflict/sqlite/go/models.go new file mode 100644 index 0000000000..e51f413f35 --- /dev/null +++ b/internal/endtoend/testdata/single_param_conflict/sqlite/go/models.go @@ -0,0 +1,19 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Author struct { + ID int64 + Name string + Bio sql.NullString +} + +type User struct { + Sub string +} diff --git a/internal/endtoend/testdata/single_param_conflict/sqlite/go/query.sql.go b/internal/endtoend/testdata/single_param_conflict/sqlite/go/query.sql.go new file mode 100644 index 0000000000..85f24715e6 --- /dev/null +++ b/internal/endtoend/testdata/single_param_conflict/sqlite/go/query.sql.go @@ -0,0 +1,50 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAuthorByID = `-- name: GetAuthorByID :one +SELECT id, name, bio +FROM authors +WHERE id = ? +LIMIT 1 +` + +func (q *Queries) GetAuthorByID(ctx context.Context, id int64) (Author, error) { + row := q.db.QueryRowContext(ctx, getAuthorByID, id) + var i Author + err := row.Scan(&i.ID, &i.Name, &i.Bio) + return i, err +} + +const getAuthorIDByID = `-- name: GetAuthorIDByID :one +SELECT id +FROM authors +WHERE id = ? +LIMIT 1 +` + +func (q *Queries) GetAuthorIDByID(ctx context.Context, id int64) (int64, error) { + row := q.db.QueryRowContext(ctx, getAuthorIDByID, id) + err := row.Scan(&id) + return id, err +} + +const getUser = `-- name: GetUser :one +SELECT sub +FROM users +WHERE sub = ? +LIMIT 1 +` + +func (q *Queries) GetUser(ctx context.Context, sub string) (string, error) { + row := q.db.QueryRowContext(ctx, getUser, sub) + err := row.Scan(&sub) + return sub, err +} diff --git a/internal/endtoend/testdata/single_param_conflict/sqlite/query.sql b/internal/endtoend/testdata/single_param_conflict/sqlite/query.sql new file mode 100644 index 0000000000..f5b577e27f --- /dev/null +++ b/internal/endtoend/testdata/single_param_conflict/sqlite/query.sql @@ -0,0 +1,29 @@ +-- Example queries for sqlc +CREATE TABLE authors ( + id BIGINT PRIMARY KEY, + name TEXT NOT NULL, + bio text +); + +-- name: GetAuthorIDByID :one +SELECT id +FROM authors +WHERE id = ? +LIMIT 1; + +-- name: GetAuthorByID :one +SELECT id, name, bio +FROM authors +WHERE id = ? +LIMIT 1; + +-- https://github.com/kyleconroy/sqlc/issues/1290 +CREATE TABLE users ( + sub TEXT PRIMARY KEY +); + +-- name: GetUser :one +SELECT sub +FROM users +WHERE sub = ? +LIMIT 1; diff --git a/internal/endtoend/testdata/limit_offset/pgx/sqlc.json b/internal/endtoend/testdata/single_param_conflict/sqlite/sqlc.json similarity index 71% rename from internal/endtoend/testdata/limit_offset/pgx/sqlc.json rename to internal/endtoend/testdata/single_param_conflict/sqlite/sqlc.json index 9403bd0279..9aea94d599 100644 --- a/internal/endtoend/testdata/limit_offset/pgx/sqlc.json +++ b/internal/endtoend/testdata/single_param_conflict/sqlite/sqlc.json @@ -2,12 +2,11 @@ "version": "1", "packages": [ { + "engine": "_lemon", "path": "go", - "engine": "postgresql", - "sql_package": "pgx/v4", "name": "querytest", "schema": "query.sql", "queries": "query.sql" } ] -} +} \ No newline at end of file diff --git a/internal/endtoend/testdata/update_set/sqlite/go/db.go b/internal/endtoend/testdata/update_set/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/update_set/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/limit_offset/pgx/go/models.go b/internal/endtoend/testdata/update_set/sqlite/go/models.go similarity index 82% rename from internal/endtoend/testdata/limit_offset/pgx/go/models.go rename to internal/endtoend/testdata/update_set/sqlite/go/models.go index 0a2313d91e..891700ec8e 100644 --- a/internal/endtoend/testdata/limit_offset/pgx/go/models.go +++ b/internal/endtoend/testdata/update_set/sqlite/go/models.go @@ -7,5 +7,6 @@ package querytest import () type Foo struct { - Bar bool + Name string + Slug string } diff --git a/internal/endtoend/testdata/update_set/sqlite/go/query.sql.go b/internal/endtoend/testdata/update_set/sqlite/go/query.sql.go new file mode 100644 index 0000000000..cf6e11b8a9 --- /dev/null +++ b/internal/endtoend/testdata/update_set/sqlite/go/query.sql.go @@ -0,0 +1,24 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const updateSet = `-- name: UpdateSet :exec +UPDATE foo SET name = ? WHERE slug = ? +` + +type UpdateSetParams struct { + Name string + Slug string +} + +func (q *Queries) UpdateSet(ctx context.Context, arg UpdateSetParams) error { + _, err := q.db.ExecContext(ctx, updateSet, arg.Name, arg.Slug) + return err +} diff --git a/internal/endtoend/testdata/update_set/sqlite/query.sql b/internal/endtoend/testdata/update_set/sqlite/query.sql new file mode 100644 index 0000000000..5b2fb0e625 --- /dev/null +++ b/internal/endtoend/testdata/update_set/sqlite/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (name text not null, slug text not null); + +/* name: UpdateSet :exec */ +UPDATE foo SET name = ? WHERE slug = ?; diff --git a/internal/endtoend/testdata/update_set/sqlite/sqlc.json b/internal/endtoend/testdata/update_set/sqlite/sqlc.json new file mode 100644 index 0000000000..9aea94d599 --- /dev/null +++ b/internal/endtoend/testdata/update_set/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "engine": "_lemon", + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} \ No newline at end of file diff --git a/internal/endtoend/testdata/update_set_multiple/sqlite/go/db.go b/internal/endtoend/testdata/update_set_multiple/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/update_set_multiple/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/limit_offset/sqlite/go/models.go b/internal/endtoend/testdata/update_set_multiple/sqlite/go/models.go similarity index 82% rename from internal/endtoend/testdata/limit_offset/sqlite/go/models.go rename to internal/endtoend/testdata/update_set_multiple/sqlite/go/models.go index 0a2313d91e..891700ec8e 100644 --- a/internal/endtoend/testdata/limit_offset/sqlite/go/models.go +++ b/internal/endtoend/testdata/update_set_multiple/sqlite/go/models.go @@ -7,5 +7,6 @@ package querytest import () type Foo struct { - Bar bool + Name string + Slug string } diff --git a/internal/endtoend/testdata/update_set_multiple/sqlite/go/query.sql.go b/internal/endtoend/testdata/update_set_multiple/sqlite/go/query.sql.go new file mode 100644 index 0000000000..fd82519eef --- /dev/null +++ b/internal/endtoend/testdata/update_set_multiple/sqlite/go/query.sql.go @@ -0,0 +1,24 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const updateSetMultiple = `-- name: UpdateSetMultiple :exec +UPDATE foo SET name = ?, slug = ? +` + +type UpdateSetMultipleParams struct { + Name string + Slug string +} + +func (q *Queries) UpdateSetMultiple(ctx context.Context, arg UpdateSetMultipleParams) error { + _, err := q.db.ExecContext(ctx, updateSetMultiple, arg.Name, arg.Slug) + return err +} diff --git a/internal/endtoend/testdata/update_set_multiple/sqlite/query.sql b/internal/endtoend/testdata/update_set_multiple/sqlite/query.sql new file mode 100644 index 0000000000..a049f90c05 --- /dev/null +++ b/internal/endtoend/testdata/update_set_multiple/sqlite/query.sql @@ -0,0 +1,4 @@ +CREATE TABLE foo (name text not null, slug text not null); + +-- name: UpdateSetMultiple :exec +UPDATE foo SET name = ?, slug = ?; diff --git a/internal/endtoend/testdata/limit_offset/sqlite/sqlc.json b/internal/endtoend/testdata/update_set_multiple/sqlite/sqlc.json similarity index 100% rename from internal/endtoend/testdata/limit_offset/sqlite/sqlc.json rename to internal/endtoend/testdata/update_set_multiple/sqlite/sqlc.json From 9b444dd623276d41598161b0714b86c5f142b1cd Mon Sep 17 00:00:00 2001 From: Kazuyuki Honda Date: Sat, 18 Jun 2022 22:34:57 +0900 Subject: [PATCH 22/22] [sqlite] support nested SELECT --- .../select_nested_count/sqlite/go/db.go | 31 +++++++++++ .../select_nested_count/sqlite/go/models.go | 21 ++++++++ .../sqlite/go/query.sql.go | 54 +++++++++++++++++++ .../select_nested_count/sqlite/query.sql | 19 +++++++ .../select_nested_count/sqlite/sqlc.json | 12 +++++ internal/engine/sqlite/convert.go | 7 +++ 6 files changed, 144 insertions(+) create mode 100644 internal/endtoend/testdata/select_nested_count/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/select_nested_count/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/select_nested_count/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/select_nested_count/sqlite/query.sql create mode 100644 internal/endtoend/testdata/select_nested_count/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/select_nested_count/sqlite/go/db.go b/internal/endtoend/testdata/select_nested_count/sqlite/go/db.go new file mode 100644 index 0000000000..19557a6a5a --- /dev/null +++ b/internal/endtoend/testdata/select_nested_count/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "context" + "database/sql" +) + +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} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/select_nested_count/sqlite/go/models.go b/internal/endtoend/testdata/select_nested_count/sqlite/go/models.go new file mode 100644 index 0000000000..edc67365cb --- /dev/null +++ b/internal/endtoend/testdata/select_nested_count/sqlite/go/models.go @@ -0,0 +1,21 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package querytest + +import ( + "database/sql" +) + +type Author struct { + ID int64 + Name string + Bio sql.NullString +} + +type Book struct { + ID int64 + AuthorID int64 + Title string +} diff --git a/internal/endtoend/testdata/select_nested_count/sqlite/go/query.sql.go b/internal/endtoend/testdata/select_nested_count/sqlite/go/query.sql.go new file mode 100644 index 0000000000..d65c4cff12 --- /dev/null +++ b/internal/endtoend/testdata/select_nested_count/sqlite/go/query.sql.go @@ -0,0 +1,54 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const getAuthorsWithBooksCount = `-- name: GetAuthorsWithBooksCount :many +SELECT id, name, bio, ( + SELECT COUNT(id) FROM books + WHERE books.author_id = id +) AS books_count +FROM authors +` + +type GetAuthorsWithBooksCountRow struct { + ID int64 + Name string + Bio sql.NullString + BooksCount int64 +} + +func (q *Queries) GetAuthorsWithBooksCount(ctx context.Context) ([]GetAuthorsWithBooksCountRow, error) { + rows, err := q.db.QueryContext(ctx, getAuthorsWithBooksCount) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetAuthorsWithBooksCountRow + for rows.Next() { + var i GetAuthorsWithBooksCountRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Bio, + &i.BooksCount, + ); 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/internal/endtoend/testdata/select_nested_count/sqlite/query.sql b/internal/endtoend/testdata/select_nested_count/sqlite/query.sql new file mode 100644 index 0000000000..fb9d37dfa1 --- /dev/null +++ b/internal/endtoend/testdata/select_nested_count/sqlite/query.sql @@ -0,0 +1,19 @@ +CREATE TABLE authors ( + id bigint PRIMARY KEY, + name text NOT NULL, + bio text +); + +CREATE TABLE books ( + id bigint PRIMARY KEY, + author_id bigint NOT NULL + REFERENCES authors(id), + title text NOT NULL +); + +-- name: GetAuthorsWithBooksCount :many +SELECT *, ( + SELECT COUNT(id) FROM books + WHERE books.author_id = id +) AS books_count +FROM authors; diff --git a/internal/endtoend/testdata/select_nested_count/sqlite/sqlc.json b/internal/endtoend/testdata/select_nested_count/sqlite/sqlc.json new file mode 100644 index 0000000000..9b0e5b8391 --- /dev/null +++ b/internal/endtoend/testdata/select_nested_count/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql", + "engine": "_lemon" + } + ] +} \ No newline at end of file diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index 42af9ca860..99d66c7e12 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -560,6 +560,10 @@ func (c *cc) convertParam(n *parser.Expr_bindContext) ast.Node { return todo(n) } +func (c *cc) convertInSelectNode(n *parser.Expr_in_selectContext) ast.Node { + return c.convert(n.Select_stmt()) +} + func (c *cc) convertInsert_stmtContext(n *parser.Insert_stmtContext) ast.Node { tableName := n.Table_name().GetText() rel := &ast.RangeVar{ @@ -728,6 +732,9 @@ func (c *cc) convert(node node) ast.Node { case *parser.Expr_math_opContext: return c.convertMathOperationNode(n) + case *parser.Expr_in_selectContext: + return c.convertInSelectNode(n) + case *parser.Factored_select_stmtContext: // TODO: need to handle this return todo(n)