Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/howto/analyze.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ provided. The schema is always read from the `--schema` file.

## Flags

- `--dialect`, `-d` - The SQL dialect to use. One of `postgresql`, `mysql`, or
`sqlite`. Required.
- `--dialect`, `-d` - The SQL dialect to use. One of `postgresql`, `mysql`,
`sqlite`, or `googlesql`. Required.
- `--schema`, `-s` - Path to the schema (DDL) file. Required.
- `--ast` - Include each statement's AST in the output. Defaults to `false`.

Expand Down
2 changes: 1 addition & 1 deletion docs/howto/parse.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ provided.
## Flags

- `--dialect`, `-d` - The SQL dialect to use. One of `postgresql`, `mysql`,
`sqlite`, or `clickhouse`. Required.
`sqlite`, `clickhouse`, or `googlesql`. Required.

## Examples

Expand Down
11 changes: 8 additions & 3 deletions internal/cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ Examples:
# Analyze a SQLite query
sqlc analyze --dialect sqlite --schema schema.sql query.sql

# Analyze a GoogleSQL (BigQuery, Spanner) query
sqlc analyze --dialect googlesql --schema schema.sql query.sql

# Analyze a query piped via stdin
echo "-- name: GetAuthor :one
SELECT * FROM authors WHERE id = $1;" | sqlc analyze --dialect postgresql --schema schema.sql
Expand All @@ -50,7 +53,7 @@ Examples:
return err
}
if dialect == "" {
return fmt.Errorf("--dialect flag is required (postgresql, mysql, or sqlite)")
return fmt.Errorf("--dialect flag is required (postgresql, mysql, sqlite, or googlesql)")
}

schemaPath, err := cmd.Flags().GetString("schema")
Expand Down Expand Up @@ -107,8 +110,10 @@ Examples:
engine = config.EngineMySQL
case "sqlite":
engine = config.EngineSQLite
case "googlesql":
engine = config.EngineGoogleSQL
default:
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, or sqlite)", dialect)
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, sqlite, or googlesql)", dialect)
}

sql := config.SQL{
Expand Down Expand Up @@ -150,7 +155,7 @@ Examples:
return nil
},
}
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, or sqlite)")
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, sqlite, or googlesql)")
cmd.Flags().StringP("schema", "s", "", "path to the schema file")
cmd.Flags().BoolP("ast", "", false, "include the statement AST in the output")
return cmd
Expand Down
5 changes: 5 additions & 0 deletions internal/compiler/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/sqlc-dev/sqlc/internal/config"
"github.com/sqlc-dev/sqlc/internal/dbmanager"
"github.com/sqlc-dev/sqlc/internal/engine/dolphin"
"github.com/sqlc-dev/sqlc/internal/engine/googlesql"
"github.com/sqlc-dev/sqlc/internal/engine/postgresql"
pganalyze "github.com/sqlc-dev/sqlc/internal/engine/postgresql/analyzer"
"github.com/sqlc-dev/sqlc/internal/engine/sqlite"
Expand Down Expand Up @@ -82,6 +83,10 @@ func NewCompiler(conf config.SQL, combo config.CombinedSettings, parserOpts opts
c.parser = dolphin.NewParser()
c.catalog = dolphin.NewCatalog()
c.selector = newDefaultSelector()
case config.EngineGoogleSQL:
c.parser = googlesql.NewParser()
c.catalog = googlesql.NewCatalog()
c.selector = newDefaultSelector()
case config.EnginePostgreSQL:
parser := postgresql.NewParser()
c.parser = parser
Expand Down
2 changes: 1 addition & 1 deletion internal/compiler/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (c *Compiler) quoteIdent(ident string) string {

func (c *Compiler) quote(x string) string {
switch c.conf.Engine {
case config.EngineMySQL:
case config.EngineMySQL, config.EngineGoogleSQL:
return "`" + x + "`"
default:
return "\"" + x + "\""
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const (
EngineMySQL Engine = "mysql"
EnginePostgreSQL Engine = "postgresql"
EngineSQLite Engine = "sqlite"
EngineGoogleSQL Engine = "googlesql"
)

type Config struct {
Expand Down
5 changes: 5 additions & 0 deletions internal/endtoend/testdata/analyze_basic/googlesql/exec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"command": "analyze",
"args": ["--dialect", "googlesql", "--schema", "schema.sql", "query.sql"],
"contexts": ["base"]
}
2 changes: 2 additions & 0 deletions internal/endtoend/testdata/analyze_basic/googlesql/query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- name: GetUser :one
SELECT id, name FROM users WHERE id = @id;
5 changes: 5 additions & 0 deletions internal/endtoend/testdata/analyze_basic/googlesql/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE users (
id INT64 NOT NULL,
name STRING NOT NULL,
bio STRING,
) PRIMARY KEY (id);
34 changes: 34 additions & 0 deletions internal/endtoend/testdata/analyze_basic/googlesql/stdout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[
{
"name": "GetUser",
"cmd": ":one",
"columns": [
{
"name": "id",
"data_type": "int64",
"not_null": true,
"is_array": false,
"table": "users"
},
{
"name": "name",
"data_type": "string",
"not_null": true,
"is_array": false,
"table": "users"
}
],
"params": [
{
"number": 1,
"column": {
"name": "id",
"data_type": "int64",
"not_null": true,
"is_array": false,
"table": "users"
}
}
]
}
]
5 changes: 5 additions & 0 deletions internal/endtoend/testdata/analyze_dml/googlesql/exec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"command": "analyze",
"args": ["--dialect", "googlesql", "--schema", "schema.sql", "query.sql"],
"contexts": ["base"]
}
11 changes: 11 additions & 0 deletions internal/endtoend/testdata/analyze_dml/googlesql/query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- name: CreateUser :exec
INSERT INTO users (id, name, bio) VALUES (@id, @name, @bio);

-- name: CreateUserReturning :one
INSERT INTO users (id, name) VALUES (@id, @name) THEN RETURN id, name;

-- name: UpdateBio :exec
UPDATE users SET bio = @bio WHERE id = @id;

-- name: DeleteUser :exec
DELETE FROM users WHERE id = @id;
5 changes: 5 additions & 0 deletions internal/endtoend/testdata/analyze_dml/googlesql/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE users (
id INT64 NOT NULL,
name STRING NOT NULL,
bio STRING,
) PRIMARY KEY (id);
125 changes: 125 additions & 0 deletions internal/endtoend/testdata/analyze_dml/googlesql/stdout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
[
{
"name": "CreateUser",
"cmd": ":exec",
"columns": [],
"params": [
{
"number": 1,
"column": {
"name": "id",
"data_type": "int64",
"not_null": true,
"is_array": false,
"table": "users"
}
},
{
"number": 2,
"column": {
"name": "name",
"data_type": "string",
"not_null": true,
"is_array": false,
"table": "users"
}
},
{
"number": 3,
"column": {
"name": "bio",
"data_type": "string",
"not_null": false,
"is_array": false,
"table": "users"
}
}
]
},
{
"name": "CreateUserReturning",
"cmd": ":one",
"columns": [
{
"name": "id",
"data_type": "int64",
"not_null": true,
"is_array": false,
"table": "users"
},
{
"name": "name",
"data_type": "string",
"not_null": true,
"is_array": false,
"table": "users"
}
],
"params": [
{
"number": 1,
"column": {
"name": "id",
"data_type": "int64",
"not_null": true,
"is_array": false,
"table": "users"
}
},
{
"number": 2,
"column": {
"name": "name",
"data_type": "string",
"not_null": true,
"is_array": false,
"table": "users"
}
}
]
},
{
"name": "UpdateBio",
"cmd": ":exec",
"columns": [],
"params": [
{
"number": 1,
"column": {
"name": "bio",
"data_type": "string",
"not_null": false,
"is_array": false,
"table": "users"
}
},
{
"number": 2,
"column": {
"name": "id",
"data_type": "int64",
"not_null": true,
"is_array": false,
"table": "users"
}
}
]
},
{
"name": "DeleteUser",
"cmd": ":exec",
"columns": [],
"params": [
{
"number": 1,
"column": {
"name": "id",
"data_type": "int64",
"not_null": true,
"is_array": false,
"table": "users"
}
}
]
}
]
6 changes: 4 additions & 2 deletions internal/endtoend/testdata/parse_basic/googlesql/stdout.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[
{
"name": "GetValue",
"cmd": ":one",
"ast": {
"Stmt": {
"DistinctClause": null,
Expand Down Expand Up @@ -35,8 +37,8 @@
"Larg": null,
"Rarg": null
},
"StmtLocation": 23,
"StmtLen": 8
"StmtLocation": 0,
"StmtLen": 31
}
}
]
16 changes: 12 additions & 4 deletions internal/engine/googlesql/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,8 @@ func (c *cc) convertInsertStatement(n *zjast.InsertStatement) ast.Node {
return todo(n)
}
stmt := &ast.InsertStmt{
Relation: parseRangeVar(path),
Relation: parseRangeVar(path),
ReturningList: &ast.List{},
}

if n.Columns != nil {
Expand All @@ -770,6 +771,10 @@ func (c *cc) convertInsertStatement(n *zjast.InsertStatement) ast.Node {
switch {
case n.Rows != nil:
selectStmt := &ast.SelectStmt{
// A non-nil TargetList matches the shape the other engines produce
// for INSERT ... VALUES; the compiler's parameter search iterates it
// unconditionally.
TargetList: &ast.List{},
ValuesLists: &ast.List{},
}
for _, row := range n.Rows.Rows {
Expand Down Expand Up @@ -816,8 +821,10 @@ func (c *cc) convertUpdateStatement(n *zjast.UpdateStatement) ast.Node {
rv := parseRangeVar(path)
rv.Alias = convertAlias(n.Alias)
stmt := &ast.UpdateStmt{
Relations: &ast.List{Items: []ast.Node{rv}},
TargetList: &ast.List{},
Relations: &ast.List{Items: []ast.Node{rv}},
TargetList: &ast.List{},
FromClause: &ast.List{},
ReturningList: &ast.List{},
}

if n.UpdateItemList != nil {
Expand Down Expand Up @@ -869,7 +876,8 @@ func (c *cc) convertDeleteStatement(n *zjast.DeleteStatement) ast.Node {
rv := parseRangeVar(path)
rv.Alias = convertAlias(n.Alias)
stmt := &ast.DeleteStmt{
Relations: &ast.List{Items: []ast.Node{rv}},
Relations: &ast.List{Items: []ast.Node{rv}},
ReturningList: &ast.List{},
}

if n.Where != nil {
Expand Down
15 changes: 12 additions & 3 deletions internal/engine/googlesql/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,32 @@ func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
return nil, err
}

// zetajones node locations are byte offsets into the input. Following the
// convention used by the other engines, a statement's location starts at the
// end of the previous statement (or the start of the input) so that any
// leading comment — in particular the "-- name:" annotation sqlc relies on —
// is captured as part of the statement.
var stmts []ast.Statement
loc := 0
for _, stmt := range stmtNodes {
converter := &cc{}
out := converter.convert(stmt)
if _, ok := out.(*ast.TODO); ok {
// Skip over the unsupported statement (and its trailing semicolon)
// so the next statement's leading comment is measured from here.
loc = stmt.End() + 1
continue
}

// zetajones node locations are byte offsets into the input.
loc := stmt.Pos()
end := stmt.End()
stmts = append(stmts, ast.Statement{
Raw: &ast.RawStmt{
Stmt: out,
StmtLocation: loc,
StmtLen: stmt.End() - loc,
StmtLen: end - loc,
},
})
loc = end + 1
}

return stmts, nil
Expand Down
Loading
Loading