Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support json functions #20

Merged
merged 5 commits into from
Aug 26, 2022
Merged
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
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ A list of ZetaSQL specifications and features supported by go-zetasqlite.
- [x] ARRAY
- [x] STRUCT
- [ ] GEOGRAPHY
- [ ] JSON
- [x] JSON
- [x] RECORD

## Statements
Expand Down Expand Up @@ -369,8 +369,13 @@ A list of ZetaSQL specifications and features supported by go-zetasqlite.
- [ ] JSON_EXTRACT_STRING_ARRAY
- [ ] JSON_VALUE_ARRAY
- [ ] PARSE_JSON
- [ ] TO_JSON
- [x] TO_JSON
- [ ] TO_JSON_STRING
- [ ] STRING
- [x] BOOL
- [x] INT64
- [x] FLOAT64
- [x] JSON_TYPE

### Array functions

Expand Down
5 changes: 0 additions & 5 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"sync"

"github.com/goccy/go-zetasql"
internal "github.com/goccy/go-zetasqlite/internal"
"github.com/mattn/go-sqlite3"
)
Expand Down Expand Up @@ -103,10 +102,6 @@ func (c *ZetaSQLiteConn) AddNamePath(path string) {
c.analyzer.AddNamePath(path)
}

func (c *ZetaSQLiteConn) SetParameterMode(mode zetasql.ParameterMode) {
c.analyzer.SetParameterMode(mode)
}

func (s *ZetaSQLiteConn) CheckNamedValue(value *driver.NamedValue) error {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestRegisterCustomDriver(t *testing.T) {
if _, err := db.Exec("INSERT `project-id`.datasetID.tableID (Id) VALUES (1)"); err != nil {
t.Fatal(err)
}
row := db.QueryRow("SELECT * FROM project-id.datasetID.tableID WHERE Id = @id", 1)
row := db.QueryRow("SELECT * FROM project-id.datasetID.tableID WHERE Id = ?", 1)
if row.Err() != nil {
t.Fatal(row.Err())
}
Expand Down
9 changes: 9 additions & 0 deletions exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ func TestExec(t *testing.T) {
name: "create table with all types",
query: `CREATE TABLE _table_a ( doubleValue DOUBLE, floatValue FLOAT )`,
},
{
name: "recreate table",
query: `
CREATE OR REPLACE TABLE recreate_table ( a string );
DROP TABLE recreate_table;
CREATE TABLE recreate_table ( b string );
INSERT recreate_table (b) VALUES ('hello');
`,
},
{
name: "transaction",
query: `
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/goccy/go-zetasqlite
go 1.17

require (
github.com/goccy/go-zetasql v0.3.2
github.com/goccy/go-zetasql v0.3.3
github.com/mattn/go-sqlite3 v1.14.14
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhO
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc=
github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-zetasql v0.3.2 h1:+HxuazroaYJnZYZdBgeRwxnRmXO+OTE+EMQ9hQwHhUA=
github.com/goccy/go-zetasql v0.3.2/go.mod h1:6W14CJVKh7crrSPyj6NPk4c49L2NWnxvyDLsRkOm4BI=
github.com/goccy/go-zetasql v0.3.3 h1:+Ar/GZ4k2vNgaljRPt5lpD8JSIiq0WSEG38Fbowj5fM=
github.com/goccy/go-zetasql v0.3.3/go.mod h1:6W14CJVKh7crrSPyj6NPk4c49L2NWnxvyDLsRkOm4BI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
Expand Down
66 changes: 56 additions & 10 deletions internal/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@ func (a *Analyzer) AddNamePath(path string) {
a.namePath = append(a.namePath, path)
}

func (a *Analyzer) SetParameterMode(mode zetasql.ParameterMode) {
a.opt.SetParameterMode(mode)
}

func (a *Analyzer) parseScript(query string) ([]parsed_ast.StatementNode, error) {
loc := zetasql.NewParseResumeLocation(query)
var stmts []parsed_ast.StatementNode
Expand Down Expand Up @@ -201,6 +197,32 @@ func (a *Analyzer) getFullNamePathMap(stmts []parsed_ast.StatementNode) (map[str
return fullNamePathMap, nil
}

func (a *Analyzer) getParameterMode(stmt parsed_ast.StatementNode) (zetasql.ParameterMode, error) {
var (
enabledNamedParameter bool
enabledPositionalParameter bool
)
parsed_ast.Walk(stmt, func(node parsed_ast.Node) error {
switch n := node.(type) {
case *parsed_ast.ParameterExprNode:
if n.Position() > 0 {
enabledPositionalParameter = true
}
if n.Name() != nil {
enabledNamedParameter = true
}
}
return nil
})
if enabledNamedParameter && enabledPositionalParameter {
return zetasql.ParameterNone, fmt.Errorf("named parameter and positional parameter cannot be used together")
}
if enabledPositionalParameter {
return zetasql.ParameterPositional, nil
}
return zetasql.ParameterNamed, nil
}

func (a *Analyzer) AnalyzeIterator(ctx context.Context, conn *Conn, query string, args []driver.NamedValue) (*AnalyzerOutputIterator, error) {
if err := a.catalog.Sync(ctx, conn); err != nil {
return nil, fmt.Errorf("failed to sync catalog: %w", err)
Expand All @@ -209,32 +231,48 @@ func (a *Analyzer) AnalyzeIterator(ctx context.Context, conn *Conn, query string
if err != nil {
return nil, err
}
resultStmts := make([]*Statement, 0, len(stmts))
fullNamePathMap, err := a.getFullNamePathMap(stmts)
if err != nil {
return nil, fmt.Errorf("failed to get full name path map %s: %w", query, err)
}
for _, stmt := range stmts {
mode, err := a.getParameterMode(stmt)
if err != nil {
return nil, err
}
resultStmts = append(resultStmts, &Statement{
stmt: stmt,
mode: mode,
})
}
funcMap := map[string]*FunctionSpec{}
for _, spec := range a.catalog.getFunctions(a.namePath) {
funcMap[spec.FuncName()] = spec
}
return &AnalyzerOutputIterator{
query: query,
args: args,
stmts: stmts,
stmts: resultStmts,
analyzer: a,
funcMap: funcMap,
fullNamePathMap: fullNamePathMap,
}, nil
}

type Statement struct {
stmt parsed_ast.StatementNode
mode zetasql.ParameterMode
}

type AnalyzerOutputIterator struct {
query string
args []driver.NamedValue
analyzer *Analyzer
stmts []parsed_ast.StatementNode
stmts []*Statement
stmtIdx int
funcMap map[string]*FunctionSpec
fullNamePathMap map[string][]string
funcMap map[string]*FunctionSpec
out *zetasql.AnalyzerOutput
isEnd bool
err error
Expand All @@ -244,9 +282,11 @@ func (it *AnalyzerOutputIterator) Next() bool {
if it.stmtIdx >= len(it.stmts) {
return false
}
stmt := it.stmts[it.stmtIdx]
it.analyzer.opt.SetParameterMode(stmt.mode)
out, err := zetasql.AnalyzeStatementFromParserAST(
it.query,
it.stmts[it.stmtIdx],
stmt.stmt,
it.analyzer.catalog.getCatalog(it.analyzer.namePath),
it.analyzer.opt,
)
Expand Down Expand Up @@ -341,7 +381,10 @@ func (it *AnalyzerOutputIterator) analyzeCreateTableStmt(ctx context.Context, no
if err != nil {
return nil, err
}
it.stmts = append(it.stmts, stmt)
it.stmts = append(it.stmts, &Statement{
stmt: stmt,
mode: zetasql.ParameterNamed,
})
}
return nil, nil
},
Expand Down Expand Up @@ -394,7 +437,10 @@ func (it *AnalyzerOutputIterator) analyzeCreateTableAsSelectStmt(ctx context.Con
if err != nil {
return nil, err
}
it.stmts = append(it.stmts, stmt)
it.stmts = append(it.stmts, &Statement{
stmt: stmt,
mode: zetasql.ParameterNamed,
})
}
return nil, nil
},
Expand Down
89 changes: 76 additions & 13 deletions internal/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ DELETE FROM zetasqlite_catalog WHERE name = @name
type CatalogSpecKind string

const (
TableSpecKind CatalogSpecKind = "table"
FunctionSpecKind CatalogSpecKind = "function"
TableSpecKind CatalogSpecKind = "table"
FunctionSpecKind CatalogSpecKind = "function"
defaultCatalogName = "zetasqlite"
)

type Catalog struct {
Expand All @@ -64,12 +65,16 @@ type Catalog struct {
funcMap map[string]*FunctionSpec
}

func newSimpleCatalog(name string) *types.SimpleCatalog {
catalog := types.NewSimpleCatalog(name)
catalog.AddZetaSQLBuiltinFunctions(nil)
return catalog
}

func NewCatalog(db *sql.DB) *Catalog {
catalog := types.NewSimpleCatalog("zetasqlite")
catalog.AddZetaSQLBuiltinFunctions()
return &Catalog{
db: db,
defaultCatalog: catalog,
defaultCatalog: newSimpleCatalog(defaultCatalogName),
pathToCatalogMap: map[string]*types.SimpleCatalog{},
tableMap: map[string]*TableSpec{},
funcMap: map[string]*FunctionSpec{},
Expand Down Expand Up @@ -185,7 +190,9 @@ func (c *Catalog) DeleteTableSpec(ctx context.Context, conn *Conn, name string)
c.mu.Lock()
defer c.mu.Unlock()

// TODO: need to remove table from *types.SimpleCatalog
if err := c.deleteTableSpecByName(name); err != nil {
return err
}
if _, err := conn.ExecContext(ctx, deleteCatalogQuery, sql.Named("name", name)); err != nil {
return err
}
Expand All @@ -196,13 +203,71 @@ func (c *Catalog) DeleteFunctionSpec(ctx context.Context, conn *Conn, name strin
c.mu.Lock()
defer c.mu.Unlock()

// TODO: need to remove function from *types.SimpleCatalog
if err := c.deleteFunctionSpecByName(name); err != nil {
return err
}
if _, err := conn.ExecContext(ctx, deleteCatalogQuery, sql.Named("name", name)); err != nil {
return err
}
return nil
}

func (c *Catalog) deleteTableSpecByName(name string) error {
spec, exists := c.tableMap[name]
if !exists {
return fmt.Errorf("failed to find table spec from map by %s", name)
}
tables := make([]*TableSpec, 0, len(c.tables))
for _, table := range c.tables {
if spec == table {
continue
}
tables = append(tables, table)
}
c.tables = tables
delete(c.tableMap, name)
if err := c.resetCatalogs(); err != nil {
return fmt.Errorf("failed to reset catalogs: %w", err)
}
return nil
}

func (c *Catalog) deleteFunctionSpecByName(name string) error {
spec, exists := c.funcMap[name]
if !exists {
return fmt.Errorf("failed to find function spec from map by %s", name)
}
functions := make([]*FunctionSpec, 0, len(c.functions))
for _, function := range c.functions {
if spec == function {
continue
}
functions = append(functions, function)
}
c.functions = functions
delete(c.funcMap, name)
if err := c.resetCatalogs(); err != nil {
return fmt.Errorf("failed to reset catalogs: %w", err)
}
return nil
}

func (c *Catalog) resetCatalogs() error {
c.defaultCatalog = newSimpleCatalog(defaultCatalogName)
c.pathToCatalogMap = map[string]*types.SimpleCatalog{}
for _, spec := range c.tables {
if err := c.addTableSpec(spec); err != nil {
return err
}
}
for _, spec := range c.functions {
if err := c.addFunctionSpec(spec); err != nil {
return err
}
}
return nil
}

func (c *Catalog) saveTableSpec(ctx context.Context, conn *Conn, spec *TableSpec) error {
encoded, err := json.Marshal(spec)
if err != nil {
Expand Down Expand Up @@ -290,8 +355,7 @@ func (c *Catalog) addFunctionSpec(spec *FunctionSpec) error {
catalogMapKey := c.pathToCatalogMapKey(c.trimmedLastPath(spec.NamePath))
cat, exists := c.pathToCatalogMap[catalogMapKey]
if !exists {
cat = types.NewSimpleCatalog("zetasqlite")
cat.AddZetaSQLBuiltinFunctions()
cat = newSimpleCatalog(defaultCatalogName)
c.pathToCatalogMap[catalogMapKey] = cat
}
if err := c.addFunctionSpecRecursive(cat, spec); err != nil {
Expand All @@ -314,8 +378,7 @@ func (c *Catalog) addTableSpec(spec *TableSpec) error {
catalogMapKey := c.pathToCatalogMapKey(c.trimmedLastPath(spec.NamePath))
cat, exists := c.pathToCatalogMap[catalogMapKey]
if !exists {
cat = types.NewSimpleCatalog("zetasqlite")
cat.AddZetaSQLBuiltinFunctions()
cat = newSimpleCatalog(defaultCatalogName)
c.pathToCatalogMap[catalogMapKey] = cat
}
if err := c.addTableSpecRecursive(cat, spec); err != nil {
Expand All @@ -330,7 +393,7 @@ func (c *Catalog) addTableSpec(spec *TableSpec) error {
func (c *Catalog) addTableSpecRecursive(cat *types.SimpleCatalog, spec *TableSpec) error {
if len(spec.NamePath) > 1 {
subCatalogName := spec.NamePath[0]
subCatalog := types.NewSimpleCatalog(subCatalogName)
subCatalog := newSimpleCatalog(subCatalogName)
if !c.existsCatalog(cat, subCatalogName) {
cat.AddCatalog(subCatalog)
}
Expand Down Expand Up @@ -386,7 +449,7 @@ func (c *Catalog) createSimpleTable(tableName string, spec *TableSpec) (*types.S
func (c *Catalog) addFunctionSpecRecursive(cat *types.SimpleCatalog, spec *FunctionSpec) error {
if len(spec.NamePath) > 1 {
subCatalogName := spec.NamePath[0]
subCatalog := types.NewSimpleCatalog(subCatalogName)
subCatalog := newSimpleCatalog(subCatalogName)
if !c.existsCatalog(cat, subCatalogName) {
cat.AddCatalog(subCatalog)
}
Expand Down
Loading