diff --git a/Makefile b/Makefile index b8745e57dc..18d6ca91b5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build build-endtoend test test-ci test-examples test-endtoend start psql mysqlsh proto +.PHONY: build build-endtoend test test-ci test-examples test-endtoend start psql mysqlsh proto sqlc-dev ydb test-examples-ydb gen-examples-ydb build: go build ./... @@ -18,13 +18,21 @@ vet: test-examples: go test --tags=examples ./... +ydb-examples: sqlc-dev ydb gen-examples-ydb test-examples-ydb + +test-examples-ydb: + YDB_SERVER_URI=localhost:2136 go test -v ./examples/authors/ydb/... -count=1 + +gen-examples-ydb: + cd examples/authors/ && SQLCDEBUG=1 ~/bin/sqlc-dev generate && cd ../.. + build-endtoend: cd ./internal/endtoend/testdata && go build ./... test-ci: test-examples build-endtoend vet sqlc-dev: - go build -o ~/bin/sqlc-dev ./cmd/sqlc/ + go build -x -v -o ~/bin/sqlc-dev ./cmd/sqlc/ sqlc-pg-gen: go build -o ~/bin/sqlc-pg-gen ./internal/tools/sqlc-pg-gen @@ -38,6 +46,9 @@ test-json-process-plugin: start: docker compose up -d +ydb: + docker compose up -d ydb + fmt: go fmt ./... diff --git a/docker-compose.yml b/docker-compose.yml index f318d1ed93..255527a3d1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,3 +19,18 @@ services: POSTGRES_DB: postgres POSTGRES_PASSWORD: mysecretpassword POSTGRES_USER: postgres + + ydb: + image: ydbplatform/local-ydb:latest + ports: + - "2135:2135" + - "2136:2136" + - "8765:8765" + restart: always + hostname: localhost + environment: + - YDB_USE_IN_MEMORY_PDISKS=true + - GRPC_TLS_PORT=2135 + - GRPC_PORT=2136 + - MON_PORT=8765 + diff --git a/examples/authors/sqlc.yaml b/examples/authors/sqlc.yaml index 57f2319ea1..49fe62ff76 100644 --- a/examples/authors/sqlc.yaml +++ b/examples/authors/sqlc.yaml @@ -43,6 +43,18 @@ sql: go: package: authors out: sqlite +- name: ydb + schema: ydb/schema.sql + queries: ydb/query.sql + engine: ydb + gen: + go: + package: authors + out: ydb + emit_json_tags: true + sql_package: ydb-go-sdk + + rules: - name: postgresql-query-too-costly message: "Too costly" diff --git a/examples/authors/ydb/db.go b/examples/authors/ydb/db.go new file mode 100644 index 0000000000..9a15b333ce --- /dev/null +++ b/examples/authors/ydb/db.go @@ -0,0 +1,26 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package authors + +import ( + "context" + + "github.com/ydb-platform/ydb-go-sdk/v3/query" +) + +type DBTX interface { + Exec(ctx context.Context, sql string, opts ...query.ExecuteOption) error + Query(ctx context.Context, sql string, opts ...query.ExecuteOption) (query.Result, error) + QueryResultSet(ctx context.Context, sql string, opts ...query.ExecuteOption) (query.ClosableResultSet, error) + QueryRow(ctx context.Context, sql string, opts ...query.ExecuteOption) (query.Row, error) +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} diff --git a/examples/authors/ydb/db_test.go b/examples/authors/ydb/db_test.go new file mode 100644 index 0000000000..ab5324e76d --- /dev/null +++ b/examples/authors/ydb/db_test.go @@ -0,0 +1,100 @@ +package authors + +import ( + "context" + "testing" + + "github.com/sqlc-dev/sqlc/internal/sqltest/local" + _ "github.com/ydb-platform/ydb-go-sdk/v3" + "github.com/ydb-platform/ydb-go-sdk/v3/query" +) + +func ptr(s string) *string { + return &s +} + +func TestAuthors(t *testing.T) { + ctx := context.Background() + + db := local.YDB(t, []string{"schema.sql"}) + defer db.Close(ctx) + + q := New(db.Query()) + + t.Run("InsertAuthors", func(t *testing.T) { + authorsToInsert := []CreateOrUpdateAuthorParams{ + {P0: 1, P1: "Leo Tolstoy", P2: ptr("Russian writer, author of \"War and Peace\"")}, + {P0: 2, P1: "Alexander Pushkin", P2: ptr("Author of \"Eugene Onegin\"")}, + {P0: 3, P1: "Alexander Pushkin", P2: ptr("Russian poet, playwright, and prose writer")}, + {P0: 4, P1: "Fyodor Dostoevsky", P2: ptr("Author of \"Crime and Punishment\"")}, + {P0: 5, P1: "Nikolai Gogol", P2: ptr("Author of \"Dead Souls\"")}, + {P0: 6, P1: "Anton Chekhov", P2: nil}, + {P0: 7, P1: "Ivan Turgenev", P2: ptr("Author of \"Fathers and Sons\"")}, + {P0: 8, P1: "Mikhail Lermontov", P2: nil}, + {P0: 9, P1: "Daniil Kharms", P2: ptr("Absurdist, writer and poet")}, + {P0: 10, P1: "Maxim Gorky", P2: ptr("Author of \"At the Bottom\"")}, + {P0: 11, P1: "Vladimir Mayakovsky", P2: nil}, + {P0: 12, P1: "Sergei Yesenin", P2: ptr("Russian lyric poet")}, + {P0: 13, P1: "Boris Pasternak", P2: ptr("Author of \"Doctor Zhivago\"")}, + } + + for _, author := range authorsToInsert { + if err := q.CreateOrUpdateAuthor(ctx, author, query.WithIdempotent()); err != nil { + t.Fatalf("failed to insert author %q: %v", author.P1, err) + } + } + }) + + t.Run("ListAuthors", func(t *testing.T) { + authors, err := q.ListAuthors(ctx) + if err != nil { + t.Fatal(err) + } + if len(authors) == 0 { + t.Fatal("expected at least one author, got none") + } + t.Log("Authors:") + for _, a := range authors { + bio := "Null" + if a.Bio != nil { + bio = *a.Bio + } + t.Logf("- ID: %d | Name: %s | Bio: %s", a.ID, a.Name, bio) + } + }) + + t.Run("GetAuthor", func(t *testing.T) { + singleAuthor, err := q.GetAuthor(ctx, 10) + if err != nil { + t.Fatal(err) + } + bio := "Null" + if singleAuthor.Bio != nil { + bio = *singleAuthor.Bio + } + t.Logf("- ID: %d | Name: %s | Bio: %s", singleAuthor.ID, singleAuthor.Name, bio) + }) + + t.Run("Delete All Authors", func(t *testing.T) { + var i uint64 + for i = 1; i <= 13; i++ { + if err := q.DeleteAuthor(ctx, i, query.WithIdempotent()); err != nil { + t.Fatalf("failed to delete author: %v", err) + } + } + authors, err := q.ListAuthors(ctx) + if err != nil { + t.Fatal(err) + } + if len(authors) != 0 { + t.Fatalf("expected no authors, got %d", len(authors)) + } + }) + + t.Run("Drop Table Authors", func(t *testing.T) { + err := q.DropTable(ctx) + if err != nil { + t.Fatal(err) + } + }) +} diff --git a/examples/authors/ydb/models.go b/examples/authors/ydb/models.go new file mode 100644 index 0000000000..2806beacfe --- /dev/null +++ b/examples/authors/ydb/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package authors + +type Author struct { + ID uint64 `json:"id"` + Name string `json:"name"` + Bio *string `json:"bio"` +} diff --git a/examples/authors/ydb/query.sql b/examples/authors/ydb/query.sql new file mode 100644 index 0000000000..804150615d --- /dev/null +++ b/examples/authors/ydb/query.sql @@ -0,0 +1,15 @@ +-- name: GetAuthor :one +SELECT * FROM authors +WHERE id = $p0 LIMIT 1; + +-- name: ListAuthors :many +SELECT * FROM authors ORDER BY name; + +-- name: CreateOrUpdateAuthor :exec +UPSERT INTO authors (id, name, bio) VALUES ($p0, $p1, $p2); + +-- name: DeleteAuthor :exec +DELETE FROM authors WHERE id = $p0; + +-- name: DropTable :exec +DROP TABLE IF EXISTS authors; \ No newline at end of file diff --git a/examples/authors/ydb/query.sql.go b/examples/authors/ydb/query.sql.go new file mode 100644 index 0000000000..e9b6b332a4 --- /dev/null +++ b/examples/authors/ydb/query.sql.go @@ -0,0 +1,126 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: query.sql + +package authors + +import ( + "context" + + "github.com/ydb-platform/ydb-go-sdk/v3" + "github.com/ydb-platform/ydb-go-sdk/v3/pkg/xerrors" + "github.com/ydb-platform/ydb-go-sdk/v3/query" +) + +const createOrUpdateAuthor = `-- name: CreateOrUpdateAuthor :exec +UPSERT INTO authors (id, name, bio) VALUES ($p0, $p1, $p2) +` + +type CreateOrUpdateAuthorParams struct { + P0 uint64 `json:"p0"` + P1 string `json:"p1"` + P2 *string `json:"p2"` +} + +func (q *Queries) CreateOrUpdateAuthor(ctx context.Context, arg CreateOrUpdateAuthorParams, opts ...query.ExecuteOption) error { + err := q.db.Exec(ctx, createOrUpdateAuthor, + append(opts, + query.WithParameters( + ydb.ParamsBuilder(). + Param("$p0").Uint64(arg.P0). + Param("$p1").Text(arg.P1). + Param("$p2").BeginOptional().Text(arg.P2).EndOptional(). + Build(), + ), + )..., + ) + if err != nil { + return xerrors.WithStackTrace(err) + } + return nil +} + +const deleteAuthor = `-- name: DeleteAuthor :exec +DELETE FROM authors WHERE id = $p0 +` + +func (q *Queries) DeleteAuthor(ctx context.Context, p0 uint64, opts ...query.ExecuteOption) error { + err := q.db.Exec(ctx, deleteAuthor, + append(opts, + query.WithParameters( + ydb.ParamsBuilder(). + Param("$p0").Uint64(p0). + Build(), + ), + )..., + ) + if err != nil { + return xerrors.WithStackTrace(err) + } + return nil +} + +const dropTable = `-- name: DropTable :exec +DROP TABLE IF EXISTS authors +` + +func (q *Queries) DropTable(ctx context.Context, opts ...query.ExecuteOption) error { + err := q.db.Exec(ctx, dropTable, opts...) + if err != nil { + return xerrors.WithStackTrace(err) + } + return nil +} + +const getAuthor = `-- name: GetAuthor :one +SELECT id, name, bio FROM authors +WHERE id = $p0 LIMIT 1 +` + +func (q *Queries) GetAuthor(ctx context.Context, p0 uint64, opts ...query.ExecuteOption) (Author, error) { + row, err := q.db.QueryRow(ctx, getAuthor, + append(opts, + query.WithParameters( + ydb.ParamsBuilder(). + Param("$p0").Uint64(p0). + Build(), + ), + )..., + ) + var i Author + if err != nil { + return i, xerrors.WithStackTrace(err) + } + err = row.Scan(&i.ID, &i.Name, &i.Bio) + if err != nil { + return i, xerrors.WithStackTrace(err) + } + return i, nil +} + +const listAuthors = `-- name: ListAuthors :many +SELECT id, name, bio FROM authors ORDER BY name +` + +func (q *Queries) ListAuthors(ctx context.Context, opts ...query.ExecuteOption) ([]Author, error) { + result, err := q.db.QueryResultSet(ctx, listAuthors, opts...) + if err != nil { + return nil, xerrors.WithStackTrace(err) + } + var items []Author + for row, err := range result.Rows(ctx) { + if err != nil { + return nil, xerrors.WithStackTrace(err) + } + var i Author + if err := row.Scan(&i.ID, &i.Name, &i.Bio); err != nil { + return nil, xerrors.WithStackTrace(err) + } + items = append(items, i) + } + if err := result.Close(ctx); err != nil { + return nil, xerrors.WithStackTrace(err) + } + return items, nil +} diff --git a/examples/authors/ydb/schema.sql b/examples/authors/ydb/schema.sql new file mode 100644 index 0000000000..5207fb3b1e --- /dev/null +++ b/examples/authors/ydb/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE authors ( + id Uint64, + name Utf8 NOT NULL, + bio Utf8, + PRIMARY KEY (id) +); diff --git a/go.mod b/go.mod index 34aaa12c5f..4b755a4baa 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,8 @@ require ( github.com/tetratelabs/wazero v1.9.0 github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 github.com/xeipuuv/gojsonschema v1.2.0 + github.com/ydb-platform/ydb-go-sdk/v3 v3.115.3 + github.com/ydb-platform/yql-parsers v0.0.0-20250911122629-e8a65d734cbd golang.org/x/sync v0.16.0 google.golang.org/grpc v1.75.0 google.golang.org/protobuf v1.36.8 @@ -35,6 +37,7 @@ require ( cel.dev/expr v0.24.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -45,6 +48,7 @@ require ( github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pingcap/errors v0.11.5-0.20240311024730-e056997136bb // indirect @@ -56,6 +60,7 @@ require ( github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect diff --git a/go.sum b/go.sum index fd8d405059..eb68917f04 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,24 @@ cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -20,8 +32,15 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= @@ -33,19 +52,43 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -101,6 +144,8 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -143,10 +188,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM= +github.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/riza-io/grpc-go v0.2.0 h1:2HxQKFVE7VuYstcJ8zqpN84VnAoJ4dCL6YFhJewNcHQ= github.com/riza-io/grpc-go v0.2.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= @@ -175,8 +224,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfPe7z7go8Dvv1AJQDI3eQ/5xith3q2mFlo= @@ -189,6 +238,12 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77 h1:LY6cI8cP4B9rrpTleZk95+08kl2gF4rixG7+V/dwL6Q= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= +github.com/ydb-platform/ydb-go-sdk/v3 v3.115.3 h1:SFeSK2c+PmiToyNIhr143u+YDzLhl/kboXwKLYDk0O4= +github.com/ydb-platform/ydb-go-sdk/v3 v3.115.3/go.mod h1:Pp1w2xxUoLQ3NCNAwV7pvDq0TVQOdtAqs+ZiC+i8r14= +github.com/ydb-platform/yql-parsers v0.0.0-20250911122629-e8a65d734cbd h1:ZfUkkZ1m5JCAw7jHQavecv+gKJWA6SNxuKLqHQ5/988= +github.com/ydb-platform/yql-parsers v0.0.0-20250911122629-e8a65d734cbd/go.mod h1:vrPJPS8cdPSV568YcXhB4bUwhyV8bmWKqmQ5c5Xi99o= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= @@ -202,6 +257,7 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -213,6 +269,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -238,23 +296,39 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -265,7 +339,10 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= @@ -280,8 +357,11 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -298,13 +378,38 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= @@ -318,11 +423,14 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM= modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= diff --git a/internal/codegen/golang/driver.go b/internal/codegen/golang/driver.go index 5e3a533dcc..6e0596172f 100644 --- a/internal/codegen/golang/driver.go +++ b/internal/codegen/golang/driver.go @@ -8,6 +8,8 @@ func parseDriver(sqlPackage string) opts.SQLDriver { return opts.SQLDriverPGXV4 case opts.SQLPackagePGXV5: return opts.SQLDriverPGXV5 + case opts.SQLPackageYDBGoSDK: + return opts.SQLDriverYDBGoSDK default: return opts.SQLDriverLibPQ } diff --git a/internal/codegen/golang/gen.go b/internal/codegen/golang/gen.go index 7df56a0a41..4b48f34bde 100644 --- a/internal/codegen/golang/gen.go +++ b/internal/codegen/golang/gen.go @@ -209,6 +209,15 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum, return nil, errors.New(":batch* commands are only supported by pgx") } + if tctx.SQLDriver.IsYDBGoSDK() { + for _, q := range queries { + switch q.Cmd { + case metadata.CmdExecResult, metadata.CmdExecRows, metadata.CmdExecLastId: + return nil, fmt.Errorf("%s is not supported by ydb-go-sdk", q.Cmd) + } + } + } + funcMap := template.FuncMap{ "lowerTitle": sdk.LowerTitle, "comment": sdk.DoubleSlashComment, diff --git a/internal/codegen/golang/go_type.go b/internal/codegen/golang/go_type.go index c4aac84dd6..11eb8931df 100644 --- a/internal/codegen/golang/go_type.go +++ b/internal/codegen/golang/go_type.go @@ -89,6 +89,8 @@ func goInnerType(req *plugin.GenerateRequest, options *opts.Options, col *plugin return postgresType(req, options, col) case "sqlite": return sqliteType(req, options, col) + case "ydb": + return YDBType(req, options, col) default: return "interface{}" } diff --git a/internal/codegen/golang/imports.go b/internal/codegen/golang/imports.go index ccca4f603c..17e426b8f9 100644 --- a/internal/codegen/golang/imports.go +++ b/internal/codegen/golang/imports.go @@ -132,6 +132,8 @@ func (i *importer) dbImports() fileImports { case opts.SQLDriverPGXV5: pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5/pgconn"}) pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5"}) + case opts.SQLDriverYDBGoSDK: + pkg = append(pkg, ImportSpec{Path: "github.com/ydb-platform/ydb-go-sdk/v3/query"}) default: std = append(std, ImportSpec{Path: "database/sql"}) if i.Options.EmitPreparedQueries { @@ -177,7 +179,9 @@ func buildImports(options *opts.Options, queries []Query, uses func(string) bool case opts.SQLDriverPGXV5: pkg[ImportSpec{Path: "github.com/jackc/pgx/v5/pgconn"}] = struct{}{} default: - std["database/sql"] = struct{}{} + if !sqlpkg.IsYDBGoSDK() { + std["database/sql"] = struct{}{} + } } } } @@ -267,6 +271,11 @@ func (i *importer) interfaceImports() fileImports { }) std["context"] = struct{}{} + + sqlpkg := parseDriver(i.Options.SqlPackage) + if sqlpkg.IsYDBGoSDK() { + pkg[ImportSpec{Path: "github.com/ydb-platform/ydb-go-sdk/v3/query"}] = struct{}{} + } return sortedImports(std, pkg) } @@ -395,13 +404,28 @@ func (i *importer) queryImports(filename string) fileImports { } sqlpkg := parseDriver(i.Options.SqlPackage) - if sqlcSliceScan() && !sqlpkg.IsPGX() { + if sqlcSliceScan() && !sqlpkg.IsPGX() && !sqlpkg.IsYDBGoSDK() { std["strings"] = struct{}{} } - if sliceScan() && !sqlpkg.IsPGX() { + if sliceScan() && !sqlpkg.IsPGX() && !sqlpkg.IsYDBGoSDK() { pkg[ImportSpec{Path: "github.com/lib/pq"}] = struct{}{} } + if sqlpkg.IsYDBGoSDK() { + hasParams := false + for _, q := range gq { + if !q.Arg.isEmpty() { + hasParams = true + break + } + } + if hasParams { + pkg[ImportSpec{Path: "github.com/ydb-platform/ydb-go-sdk/v3"}] = struct{}{} + } + pkg[ImportSpec{Path: "github.com/ydb-platform/ydb-go-sdk/v3/query"}] = struct{}{} + pkg[ImportSpec{Path: "github.com/ydb-platform/ydb-go-sdk/v3/pkg/xerrors"}] = struct{}{} + } + if i.Options.WrapErrors { std["fmt"] = struct{}{} } diff --git a/internal/codegen/golang/opts/enum.go b/internal/codegen/golang/opts/enum.go index 40457d040a..4d57a080a8 100644 --- a/internal/codegen/golang/opts/enum.go +++ b/internal/codegen/golang/opts/enum.go @@ -8,12 +8,14 @@ const ( SQLPackagePGXV4 string = "pgx/v4" SQLPackagePGXV5 string = "pgx/v5" SQLPackageStandard string = "database/sql" + SQLPackageYDBGoSDK string = "ydb-go-sdk" ) var validPackages = map[string]struct{}{ string(SQLPackagePGXV4): {}, string(SQLPackagePGXV5): {}, string(SQLPackageStandard): {}, + string(SQLPackageYDBGoSDK): {}, } func validatePackage(sqlPackage string) error { @@ -28,6 +30,7 @@ const ( SQLDriverPGXV5 = "github.com/jackc/pgx/v5" SQLDriverLibPQ = "github.com/lib/pq" SQLDriverGoSQLDriverMySQL = "github.com/go-sql-driver/mysql" + SQLDriverYDBGoSDK = "github.com/ydb-platform/ydb-go-sdk/v3" ) var validDrivers = map[string]struct{}{ @@ -35,6 +38,7 @@ var validDrivers = map[string]struct{}{ string(SQLDriverPGXV5): {}, string(SQLDriverLibPQ): {}, string(SQLDriverGoSQLDriverMySQL): {}, + string(SQLDriverYDBGoSDK): {}, } func validateDriver(sqlDriver string) error { @@ -52,12 +56,18 @@ func (d SQLDriver) IsGoSQLDriverMySQL() bool { return d == SQLDriverGoSQLDriverMySQL } +func (d SQLDriver) IsYDBGoSDK() bool { + return d == SQLDriverYDBGoSDK +} + func (d SQLDriver) Package() string { switch d { case SQLDriverPGXV4: return SQLPackagePGXV4 case SQLDriverPGXV5: return SQLPackagePGXV5 + case SQLDriverYDBGoSDK: + return SQLPackageYDBGoSDK default: return SQLPackageStandard } diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index 3b4fb2fa1a..52be2ecceb 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/sqlc-dev/sqlc/internal/codegen/golang/opts" + "github.com/sqlc-dev/sqlc/internal/codegen/sdk" "github.com/sqlc-dev/sqlc/internal/metadata" "github.com/sqlc-dev/sqlc/internal/plugin" ) @@ -39,6 +40,10 @@ func (v QueryValue) isEmpty() bool { return v.Typ == "" && v.Name == "" && v.Struct == nil } +func (v QueryValue) IsEmpty() bool { + return v.isEmpty() +} + type Argument struct { Name string Type string @@ -254,6 +259,158 @@ func (v QueryValue) VariableForField(f Field) string { return v.Name + "." + f.Name } +func addDollarPrefix(name string) string { + if name == "" { + return name + } + if strings.HasPrefix(name, "$") { + return name + } + return "$" + name +} + +// YDBParamMapEntries returns entries for a map[string]any literal for YDB parameters. +func (v QueryValue) YDBParamMapEntries() string { + if v.isEmpty() { + return "" + } + + var parts []string + for _, field := range v.getParameterFields() { + if field.Column != nil && field.Column.IsNamedParam { + name := field.Column.GetName() + if name != "" { + key := fmt.Sprintf("%q", addDollarPrefix(name)) + variable := v.VariableForField(field) + parts = append(parts, key+": "+escape(variable)) + } + } + } + + if len(parts) == 0 { + return "" + } + + parts = append(parts, "") + return "\n" + strings.Join(parts, ",\n") +} + +// ydbBuilderMethodForColumnType maps a YDB column data type to a ParamsBuilder method name. +func ydbBuilderMethodForColumnType(dbType string) string { + baseType := extractBaseType(strings.ToLower(dbType)) + + switch baseType { + case "bool": + return "Bool" + case "uint64": + return "Uint64" + case "int64": + return "Int64" + case "uint32": + return "Uint32" + case "int32": + return "Int32" + case "uint16": + return "Uint16" + case "int16": + return "Int16" + case "uint8": + return "Uint8" + case "int8": + return "Int8" + case "float": + return "Float" + case "double": + return "Double" + case "json": + return "JSON" + case "jsondocument": + return "JSONDocument" + case "utf8", "text", "string": + return "Text" + case "date": + return "Date" + case "date32": + return "Date32" + case "datetime": + return "Datetime" + case "timestamp": + return "Timestamp" + case "tzdate": + return "TzDate" + case "tzdatetime": + return "TzDatetime" + case "tztimestamp": + return "TzTimestamp" + + //TODO: support other types + default: + return "" + } +} + +// YDBParamsBuilder emits Go code that constructs YDB params using ParamsBuilder. +func (v QueryValue) YDBParamsBuilder() string { + if v.isEmpty() { + return "" + } + + var lines []string + + for _, field := range v.getParameterFields() { + if field.Column != nil && field.Column.IsNamedParam { + name := field.Column.GetName() + if name == "" { + continue + } + paramName := fmt.Sprintf("%q", addDollarPrefix(name)) + variable := escape(v.VariableForField(field)) + + var method string + if field.Column != nil && field.Column.Type != nil { + method = ydbBuilderMethodForColumnType(sdk.DataType(field.Column.Type)) + } + + goType := field.Type + isPtr := strings.HasPrefix(goType, "*") + if isPtr { + goType = strings.TrimPrefix(goType, "*") + } + + if method == "" { + panic(fmt.Sprintf("unknown YDB column type for param %s (goType=%s)", name, goType)) + } + + if isPtr { + lines = append(lines, fmt.Sprintf("\t\t\tParam(%s).BeginOptional().%s(%s).EndOptional().", paramName, method, variable)) + } else { + lines = append(lines, fmt.Sprintf("\t\t\tParam(%s).%s(%s).", paramName, method, variable)) + } + } + } + + if len(lines) == 0 { + return "" + } + + params := strings.Join(lines, "\n") + return fmt.Sprintf("\nquery.WithParameters(\n\t\tydb.ParamsBuilder().\n%s\n\t\t\tBuild(),\n\t\t),\n", params) +} + +func (v QueryValue) getParameterFields() []Field { + if v.Struct == nil { + return []Field{ + { + Name: v.Name, + DBName: v.DBName, + Type: v.Typ, + Column: v.Column, + }, + } + } + return v.Struct.Fields +} + // A struct used to generate methods and fields on the Queries struct type Query struct { Cmd string diff --git a/internal/codegen/golang/templates/template.tmpl b/internal/codegen/golang/templates/template.tmpl index afd50c01ac..f74b796349 100644 --- a/internal/codegen/golang/templates/template.tmpl +++ b/internal/codegen/golang/templates/template.tmpl @@ -25,6 +25,8 @@ import ( {{if .SQLDriver.IsPGX }} {{- template "dbCodeTemplatePgx" .}} +{{else if .SQLDriver.IsYDBGoSDK }} + {{- template "dbCodeTemplateYDB" .}} {{else}} {{- template "dbCodeTemplateStd" .}} {{end}} @@ -57,6 +59,8 @@ import ( {{define "interfaceCode"}} {{if .SQLDriver.IsPGX }} {{- template "interfaceCodePgx" .}} + {{else if .SQLDriver.IsYDBGoSDK }} + {{- template "interfaceCodeYDB" .}} {{else}} {{- template "interfaceCodeStd" .}} {{end}} @@ -188,6 +192,8 @@ import ( {{define "queryCode"}} {{if .SQLDriver.IsPGX }} {{- template "queryCodePgx" .}} +{{else if .SQLDriver.IsYDBGoSDK }} + {{- template "queryCodeYDB" .}} {{else}} {{- template "queryCodeStd" .}} {{end}} diff --git a/internal/codegen/golang/templates/ydb-go-sdk/dbCode.tmpl b/internal/codegen/golang/templates/ydb-go-sdk/dbCode.tmpl new file mode 100644 index 0000000000..f79831d2e2 --- /dev/null +++ b/internal/codegen/golang/templates/ydb-go-sdk/dbCode.tmpl @@ -0,0 +1,24 @@ +{{define "dbCodeTemplateYDB"}} +type DBTX interface { + Exec(ctx context.Context, sql string, opts ...query.ExecuteOption) error + Query(ctx context.Context, sql string, opts ...query.ExecuteOption) (query.Result, error) + QueryResultSet(ctx context.Context, sql string, opts ...query.ExecuteOption) (query.ClosableResultSet, error) + QueryRow(ctx context.Context, sql string, opts ...query.ExecuteOption) (query.Row, error) +} + +{{ if .EmitMethodsWithDBArgument}} +func New() *Queries { + return &Queries{} +{{- else -}} +func New(db DBTX) *Queries { + return &Queries{db: db} +{{- end}} +} + +type Queries struct { + {{if not .EmitMethodsWithDBArgument}} + db DBTX + {{end}} +} + +{{end}} diff --git a/internal/codegen/golang/templates/ydb-go-sdk/interfaceCode.tmpl b/internal/codegen/golang/templates/ydb-go-sdk/interfaceCode.tmpl new file mode 100644 index 0000000000..f9c06cc705 --- /dev/null +++ b/internal/codegen/golang/templates/ydb-go-sdk/interfaceCode.tmpl @@ -0,0 +1,36 @@ +{{define "interfaceCodeYDB"}} + type Querier interface { + {{- $dbtxParam := .EmitMethodsWithDBArgument -}} + {{- range .GoQueries}} + {{- if and (eq .Cmd ":one") ($dbtxParam) }} + {{range .Comments}}//{{.}} + {{end -}} + {{.MethodName}}(ctx context.Context, db DBTX, {{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) + {{- else if eq .Cmd ":one"}} + {{range .Comments}}//{{.}} + {{end -}} + {{.MethodName}}(ctx context.Context, {{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) + {{- end}} + {{- if and (eq .Cmd ":many") ($dbtxParam) }} + {{range .Comments}}//{{.}} + {{end -}} + {{.MethodName}}(ctx context.Context, db DBTX, {{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) + {{- else if eq .Cmd ":many"}} + {{range .Comments}}//{{.}} + {{end -}} + {{.MethodName}}(ctx context.Context, {{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) + {{- end}} + {{- if and (eq .Cmd ":exec") ($dbtxParam) }} + {{range .Comments}}//{{.}} + {{end -}} + {{.MethodName}}(ctx context.Context, db DBTX, {{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) error + {{- else if eq .Cmd ":exec"}} + {{range .Comments}}//{{.}} + {{end -}} + {{.MethodName}}(ctx context.Context, {{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) error + {{- end}} + {{- end}} + } + + var _ Querier = (*Queries)(nil) +{{end}} diff --git a/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl new file mode 100644 index 0000000000..c56fc953f8 --- /dev/null +++ b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl @@ -0,0 +1,136 @@ +{{define "queryCodeYDB"}} +{{range .GoQueries}} +{{if $.OutputQuery .SourceName}} +const {{.ConstantName}} = {{$.Q}}-- name: {{.MethodName}} {{.Cmd}} +{{escape .SQL}} +{{$.Q}} + +{{if .Arg.EmitStruct}} +type {{.Arg.Type}} struct { {{- range .Arg.UniqueFields}} + {{.Name}} {{.Type}} {{if .Tag}}{{$.Q}}{{.Tag}}{{$.Q}}{{end}} + {{- end}} +} +{{end}} + +{{if .Ret.EmitStruct}} +type {{.Ret.Type}} struct { {{- range .Ret.Struct.Fields}} + {{.Name}} {{.Type}} {{if .Tag}}{{$.Q}}{{.Tag}}{{$.Q}}{{end}} + {{- end}} +} +{{end}} + +{{if eq .Cmd ":one"}} +{{range .Comments}}//{{.}} +{{end -}} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) { + {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} + {{- if .Arg.IsEmpty }} + row, err := {{$dbArg}}.QueryRow(ctx, {{.ConstantName}}, opts...) + {{- else }} + row, err := {{$dbArg}}.QueryRow(ctx, {{.ConstantName}}, + append(opts, {{.Arg.YDBParamsBuilder}})..., + ) + {{- end }} + {{- if or (ne .Arg.Pair .Ret.Pair) (ne .Arg.DefineType .Ret.DefineType) }} + var {{.Ret.Name}} {{.Ret.Type}} + {{- end}} + if err != nil { + {{- if $.WrapErrors}} + return {{.Ret.ReturnName}}, xerrors.WithStackTrace(fmt.Errorf("query {{.MethodName}}: %w", err)) + {{- else }} + return {{.Ret.ReturnName}}, xerrors.WithStackTrace(err) + {{- end }} + } + err = row.Scan({{.Ret.Scan}}) + {{- if $.WrapErrors}} + if err != nil { + return {{.Ret.ReturnName}}, xerrors.WithStackTrace(fmt.Errorf("query {{.MethodName}}: %w", err)) + } + {{- else }} + if err != nil { + return {{.Ret.ReturnName}}, xerrors.WithStackTrace(err) + } + {{- end}} + return {{.Ret.ReturnName}}, nil +} +{{end}} + +{{if eq .Cmd ":many"}} +{{range .Comments}}//{{.}} +{{end -}} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) { + {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} + {{- if .Arg.IsEmpty }} + result, err := {{$dbArg}}.QueryResultSet(ctx, {{.ConstantName}}, opts...) + {{- else }} + result, err := {{$dbArg}}.QueryResultSet(ctx, {{.ConstantName}}, + append(opts, {{.Arg.YDBParamsBuilder}})..., + ) + {{- end }} + if err != nil { + {{- if $.WrapErrors}} + return nil, xerrors.WithStackTrace(fmt.Errorf("query {{.MethodName}}: %w", err)) + {{- else }} + return nil, xerrors.WithStackTrace(err) + {{- end }} + } + {{- if $.EmitEmptySlices}} + items := []{{.Ret.DefineType}}{} + {{else}} + var items []{{.Ret.DefineType}} + {{end -}} + for row, err := range result.Rows(ctx) { + if err != nil { + {{- if $.WrapErrors}} + return nil, xerrors.WithStackTrace(fmt.Errorf("query {{.MethodName}}: %w", err)) + {{- else }} + return nil, xerrors.WithStackTrace(err) + {{- end }} + } + var {{.Ret.Name}} {{.Ret.Type}} + if err := row.Scan({{.Ret.Scan}}); err != nil { + {{- if $.WrapErrors}} + return nil, xerrors.WithStackTrace(fmt.Errorf("query {{.MethodName}}: %w", err)) + {{- else }} + return nil, xerrors.WithStackTrace(err) + {{- end }} + } + items = append(items, {{.Ret.ReturnName}}) + } + if err := result.Close(ctx); err != nil { + {{- if $.WrapErrors}} + return nil, xerrors.WithStackTrace(fmt.Errorf("query {{.MethodName}}: %w", err)) + {{- else }} + return nil, xerrors.WithStackTrace(err) + {{- end }} + } + return items, nil +} +{{end}} + +{{if eq .Cmd ":exec"}} +{{range .Comments}}//{{.}} +{{end -}} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) error { + {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} + {{- if .Arg.IsEmpty }} + err := {{$dbArg}}.Exec(ctx, {{.ConstantName}}, opts...) + {{- else }} + err := {{$dbArg}}.Exec(ctx, {{.ConstantName}}, + append(opts, {{.Arg.YDBParamsBuilder}})..., + ) + {{- end }} + if err != nil { + {{- if $.WrapErrors }} + return xerrors.WithStackTrace(fmt.Errorf("query {{.MethodName}}: %w", err)) + {{- else }} + return xerrors.WithStackTrace(err) + {{- end }} + } + return nil +} +{{end}} + +{{end}} +{{end}} +{{end}} diff --git a/internal/codegen/golang/ydb_type.go b/internal/codegen/golang/ydb_type.go new file mode 100644 index 0000000000..0a4db80a3b --- /dev/null +++ b/internal/codegen/golang/ydb_type.go @@ -0,0 +1,204 @@ +package golang + +import ( + "log" + "strings" + + "github.com/sqlc-dev/sqlc/internal/codegen/golang/opts" + "github.com/sqlc-dev/sqlc/internal/codegen/sdk" + "github.com/sqlc-dev/sqlc/internal/debug" + "github.com/sqlc-dev/sqlc/internal/plugin" +) + +func YDBType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.Column) string { + columnType := strings.ToLower(sdk.DataType(col.Type)) + notNull := (col.NotNull || col.IsArray) && !isNullableType(columnType) + emitPointersForNull := options.EmitPointersForNullTypes + + columnType = extractBaseType(columnType) + + // https://ydb.tech/docs/ru/yql/reference/types/ + // ydb-go-sdk doesn't support sql.Null* yet + switch columnType { + // decimal types + case "bool": + if notNull { + return "bool" + } + if emitPointersForNull { + return "*bool" + } + // return "sql.NullBool" + return "*bool" + + case "int8": + if notNull { + return "int8" + } + if emitPointersForNull { + return "*int8" + } + // // The database/sql package does not have a sql.NullInt8 type, so we + // // use the smallest type they have which is NullInt16 + // return "sql.NullInt16" + return "*int8" + case "int16": + if notNull { + return "int16" + } + if emitPointersForNull { + return "*int16" + } + // return "sql.NullInt16" + return "*int16" + case "int", "int32": //ydb doesn't have int type, but we need it to support untyped constants + if notNull { + return "int32" + } + if emitPointersForNull { + return "*int32" + } + // return "sql.NullInt32" + return "*int32" + case "int64": + if notNull { + return "int64" + } + if emitPointersForNull { + return "*int64" + } + // return "sql.NullInt64" + return "*int64" + + case "uint8": + if emitPointersForNull { + return "*uint8" + } + return "uint8" + case "uint16": + if emitPointersForNull { + return "*uint16" + } + return "uint16" + case "uint32": + if emitPointersForNull { + return "*uint32" + } + return "uint32" + case "uint64": + if emitPointersForNull { + return "*uint64" + } + return "uint64" + + case "float": + if notNull { + return "float32" + } + if emitPointersForNull { + return "*float32" + } + // The database/sql package does not have a sql.NullFloat32 type, so we + // use the smallest type they have which is NullFloat64 + // return "sql.NullFloat64" + return "*float32" + case "double": + if notNull { + return "float64" + } + if emitPointersForNull { + return "*float64" + } + // return "sql.NullFloat64" + return "*float64" + + // string types + case "string", "utf8", "text": + if notNull { + return "string" + } + if emitPointersForNull { + return "*string" + } + return "*string" + + // serial types + case "smallserial", "serial2": + if notNull { + return "int16" + } + if emitPointersForNull { + return "*int16" + } + // return "sql.NullInt16" + return "*int16" + + case "serial", "serial4": + if notNull { + return "int32" + } + if emitPointersForNull { + return "*int32" + } + // return "sql.NullInt32" + return "*int32" + + case "bigserial", "serial8": + if notNull { + return "int64" + } + if emitPointersForNull { + return "*int64" + } + // return "sql.NullInt64" + return "*int64" + + case "json", "jsondocument": + if notNull { + return "string" + } + if emitPointersForNull { + return "*string" + } + return "*string" + + case "date", "date32", "datetime", "timestamp", "tzdate", "tztimestamp", "tzdatetime": + if notNull { + return "time.Time" + } + if emitPointersForNull { + return "*time.Time" + } + return "*time.Time" + + case "null": + // return "sql.Null" + return "interface{}" + + case "any": + return "interface{}" + + default: + if debug.Active { + log.Printf("unknown YDB type: %s\n", columnType) + } + + return "interface{}" + } + +} + +// This function extracts the base type from optional types +func extractBaseType(typeStr string) string { + if strings.HasPrefix(typeStr, "optional<") && strings.HasSuffix(typeStr, ">") { + return strings.TrimSuffix(strings.TrimPrefix(typeStr, "optional<"), ">") + } + if strings.HasSuffix(typeStr, "?") { + return strings.TrimSuffix(typeStr, "?") + } + return typeStr +} + +func isNullableType(typeStr string) bool { + return strings.HasPrefix(typeStr, "optional<") && strings.HasSuffix(typeStr, ">") || strings.HasSuffix(typeStr, "?") +} diff --git a/internal/compiler/engine.go b/internal/compiler/engine.go index f742bfd999..245552b07f 100644 --- a/internal/compiler/engine.go +++ b/internal/compiler/engine.go @@ -11,6 +11,7 @@ import ( "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" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" "github.com/sqlc-dev/sqlc/internal/opts" "github.com/sqlc-dev/sqlc/internal/sql/catalog" ) @@ -41,6 +42,10 @@ func NewCompiler(conf config.SQL, combo config.CombinedSettings) (*Compiler, err c.parser = sqlite.NewParser() c.catalog = sqlite.NewCatalog() c.selector = newSQLiteSelector() + case config.EngineYDB: + c.parser = ydb.NewParser() + c.catalog = ydb.NewCatalog() + c.selector = newDefaultSelector() case config.EngineMySQL: c.parser = dolphin.NewParser() c.catalog = dolphin.NewCatalog() diff --git a/internal/config/config.go b/internal/config/config.go index 0ff805fccd..f7df94e5f8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -54,6 +54,7 @@ const ( EngineMySQL Engine = "mysql" EnginePostgreSQL Engine = "postgresql" EngineSQLite Engine = "sqlite" + EngineYDB Engine = "ydb" ) type Config struct { diff --git a/internal/config/v_two.json b/internal/config/v_two.json index acf914997d..fd7084d6e8 100644 --- a/internal/config/v_two.json +++ b/internal/config/v_two.json @@ -38,7 +38,8 @@ "enum": [ "postgresql", "mysql", - "sqlite" + "sqlite", + "ydb" ] }, "schema": { diff --git a/internal/engine/ydb/catalog.go b/internal/engine/ydb/catalog.go new file mode 100644 index 0000000000..f191d936f3 --- /dev/null +++ b/internal/engine/ydb/catalog.go @@ -0,0 +1,19 @@ +package ydb + +import "github.com/sqlc-dev/sqlc/internal/sql/catalog" + + +func NewCatalog() *catalog.Catalog { + def := "main" + return &catalog.Catalog{ + DefaultSchema: def, + Schemas: []*catalog.Schema{ + defaultSchema(def), + }, + Extensions: map[string]struct{}{}, + } +} + +func NewTestCatalog() *catalog.Catalog { + return catalog.New("main") +} diff --git a/internal/engine/ydb/catalog_tests/alter_group_test.go b/internal/engine/ydb/catalog_tests/alter_group_test.go new file mode 100644 index 0000000000..297d9b326a --- /dev/null +++ b/internal/engine/ydb/catalog_tests/alter_group_test.go @@ -0,0 +1,122 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestAlterGroup(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: `ALTER GROUP admins RENAME TO superusers`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("admins"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("rename"), + Defaction: ast.DefElemAction(1), + Arg: &ast.String{Str: "superusers"}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `ALTER GROUP devs ADD USER alice, bob, carol`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("devs"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("rolemembers"), + Defaction: ast.DefElemAction(3), + Arg: &ast.List{ + Items: []ast.Node{ + &ast.RoleSpec{Rolename: strPtr("alice"), Roletype: ast.RoleSpecType(1)}, + &ast.RoleSpec{Rolename: strPtr("bob"), Roletype: ast.RoleSpecType(1)}, + &ast.RoleSpec{Rolename: strPtr("carol"), Roletype: ast.RoleSpecType(1)}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `ALTER GROUP ops DROP USER dan, erin`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("ops"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("rolemembers"), + Defaction: ast.DefElemAction(4), + Arg: &ast.List{ + Items: []ast.Node{ + &ast.RoleSpec{Rolename: strPtr("dan"), Roletype: ast.RoleSpecType(1)}, + &ast.RoleSpec{Rolename: strPtr("erin"), Roletype: ast.RoleSpecType(1)}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.DefElem{}, "Location"), + cmpopts.IgnoreFields(ast.RoleSpec{}, "Location"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/alter_user_test.go b/internal/engine/ydb/catalog_tests/alter_user_test.go new file mode 100644 index 0000000000..64a9891e8c --- /dev/null +++ b/internal/engine/ydb/catalog_tests/alter_user_test.go @@ -0,0 +1,153 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestAlterUser(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: `ALTER USER alice RENAME TO queen`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("alice"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("rename"), + Arg: &ast.String{Str: "queen"}, + Defaction: ast.DefElemAction(1), + }, + }, + }, + }, + }, + }, + }, + { + stmt: `ALTER USER bob LOGIN`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("bob"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("login"), + Arg: &ast.Boolean{Boolval: true}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `ALTER USER charlie NOLOGIN`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("charlie"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("nologin"), + Arg: &ast.Boolean{Boolval: false}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `ALTER USER dave PASSWORD 'qwerty'`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("dave"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("password"), + Arg: &ast.String{Str: "qwerty"}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `ALTER USER elena HASH 'abc123'`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.AlterRoleStmt{ + Role: &ast.RoleSpec{ + Rolename: strPtr("elena"), + Roletype: ast.RoleSpecType(1), + }, + Action: 1, + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("hash"), + Arg: &ast.String{Str: "abc123"}, + }, + }, + }, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.DefElem{}, "Location"), + cmpopts.IgnoreFields(ast.RoleSpec{}, "Location"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/create_group_test.go b/internal/engine/ydb/catalog_tests/create_group_test.go new file mode 100644 index 0000000000..bc8d8369fd --- /dev/null +++ b/internal/engine/ydb/catalog_tests/create_group_test.go @@ -0,0 +1,110 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestCreateGroup(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: `CREATE GROUP group1`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(3), // CREATE GROUP + Role: strPtr("group1"), + Options: &ast.List{}, + }, + }, + }, + }, + { + stmt: `CREATE GROUP group1 WITH USER alice`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(3), + Role: strPtr("group1"), + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("rolemembers"), + Arg: &ast.List{ + Items: []ast.Node{ + &ast.RoleSpec{ + Roletype: ast.RoleSpecType(1), + Rolename: strPtr("alice"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `CREATE GROUP group1 WITH USER alice, bebik`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(3), + Role: strPtr("group1"), + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("rolemembers"), + Arg: &ast.List{ + Items: []ast.Node{ + &ast.RoleSpec{ + Roletype: ast.RoleSpecType(1), + Rolename: strPtr("alice"), + }, + &ast.RoleSpec{ + Roletype: ast.RoleSpecType(1), + Rolename: strPtr("bebik"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.DefElem{}, "Location"), + cmpopts.IgnoreFields(ast.RoleSpec{}, "Location"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/create_table_test.go b/internal/engine/ydb/catalog_tests/create_table_test.go new file mode 100644 index 0000000000..7761118927 --- /dev/null +++ b/internal/engine/ydb/catalog_tests/create_table_test.go @@ -0,0 +1,166 @@ +package ydb_test + +import ( + "strconv" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func TestCreateTable(t *testing.T) { + tests := []struct { + stmt string + s *catalog.Schema + }{ + { + stmt: `CREATE TABLE users ( + id Uint64, + age Int32, + score Float, + PRIMARY KEY (id) + )`, + s: &catalog.Schema{ + Name: "main", + Tables: []*catalog.Table{ + { + Rel: &ast.TableName{Name: "users"}, + Columns: []*catalog.Column{ + { + Name: "id", + Type: ast.TypeName{Name: "Uint64"}, + IsNotNull: true, + }, + { + Name: "age", + Type: ast.TypeName{Name: "Int32"}, + }, + { + Name: "score", + Type: ast.TypeName{Name: "Float"}, + }, + }, + }, + }, + }, + }, + { + stmt: `CREATE TABLE posts ( + id Uint64, + title Utf8 NOT NULL, + content String, + metadata Json, + PRIMARY KEY (id) + )`, + s: &catalog.Schema{ + Name: "main", + Tables: []*catalog.Table{ + { + Rel: &ast.TableName{Name: "posts"}, + Columns: []*catalog.Column{ + { + Name: "id", + Type: ast.TypeName{Name: "Uint64"}, + IsNotNull: true, + }, + { + Name: "title", + Type: ast.TypeName{Name: "Utf8"}, + IsNotNull: true, + }, + { + Name: "content", + Type: ast.TypeName{Name: "String"}, + }, + { + Name: "metadata", + Type: ast.TypeName{Name: "Json"}, + }, + }, + }, + }, + }, + }, + { + stmt: `CREATE TABLE orders ( + id Uuid, + amount Decimal(22,9), + created_at Uint64, + PRIMARY KEY (id) + )`, + s: &catalog.Schema{ + Name: "main", + Tables: []*catalog.Table{ + { + Rel: &ast.TableName{Name: "orders"}, + Columns: []*catalog.Column{ + { + Name: "id", + Type: ast.TypeName{Name: "Uuid"}, + IsNotNull: true, + }, + { + Name: "amount", + Type: ast.TypeName{ + Name: "decimal", + Names: &ast.List{ + Items: []ast.Node{ + &ast.Integer{Ival: 22}, + &ast.Integer{Ival: 9}, + }, + }, + }, + }, + { + Name: "created_at", + Type: ast.TypeName{Name: "Uint64"}, + }, + }, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for i, tc := range tests { + test := tc + t.Run(strconv.Itoa(i), func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(test.stmt)) + if err != nil { + t.Log(test.stmt) + t.Fatal(err) + } + + c := ydb.NewTestCatalog() + if err := c.Build(stmts); err != nil { + t.Log(test.stmt) + t.Fatal(err) + } + + e := ydb.NewTestCatalog() + if test.s != nil { + var replaced bool + for i := range e.Schemas { + if e.Schemas[i].Name == test.s.Name { + e.Schemas[i] = test.s + replaced = true + break + } + } + if !replaced { + e.Schemas = append(e.Schemas, test.s) + } + } + + if diff := cmp.Diff(e, c, cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(catalog.Column{})); diff != "" { + t.Log(test.stmt) + t.Errorf("catalog mismatch:\n%s", diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/create_user_test.go b/internal/engine/ydb/catalog_tests/create_user_test.go new file mode 100644 index 0000000000..108d282c7c --- /dev/null +++ b/internal/engine/ydb/catalog_tests/create_user_test.go @@ -0,0 +1,129 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestCreateUser(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: `CREATE USER alice`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(2), // CREATE USER + Role: strPtr("alice"), + Options: &ast.List{}, + }, + }, + }, + }, + { + stmt: `CREATE USER bob PASSWORD 'secret'`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(2), + Role: strPtr("bob"), + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("password"), + Arg: &ast.String{Str: "secret"}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `CREATE USER charlie LOGIN`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(2), + Role: strPtr("charlie"), + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("login"), + Arg: &ast.Boolean{Boolval: true}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `CREATE USER dave NOLOGIN`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(2), + Role: strPtr("dave"), + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("nologin"), + Arg: &ast.Boolean{Boolval: false}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `CREATE USER bjorn HASH 'abc123'`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(2), + Role: strPtr("bjorn"), + Options: &ast.List{ + Items: []ast.Node{ + &ast.DefElem{ + Defname: strPtr("hash"), + Arg: &ast.String{Str: "abc123"}, + }, + }, + }, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + cmpopts.IgnoreFields(ast.DefElem{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/delete_test.go b/internal/engine/ydb/catalog_tests/delete_test.go new file mode 100644 index 0000000000..1885deb9ce --- /dev/null +++ b/internal/engine/ydb/catalog_tests/delete_test.go @@ -0,0 +1,211 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestDelete(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: "DELETE FROM users WHERE id = 1 RETURNING id", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.DeleteStmt{ + Relations: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{Relname: strPtr("users")}, + }, + }, + WhereClause: &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "="}}}, + Lexpr: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "id"}}}, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 1}, + }, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{&ast.String{Str: "id"}}, + }, + }, + }, + }, + }, + Batch: false, + OnCols: nil, + OnSelectStmt: nil, + }, + }, + }, + }, + { + stmt: "BATCH DELETE FROM users WHERE is_deleted = true RETURNING *", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.DeleteStmt{ + Relations: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{Relname: strPtr("users")}, + }, + }, + WhereClause: &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "="}}}, + Lexpr: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "is_deleted"}}}, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Boolean{Boolval: true}, + }, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.A_Star{}}}, + }, + }, + }, + }, + Batch: true, + OnCols: nil, + OnSelectStmt: nil, + }, + }, + }, + }, + { + stmt: "DELETE FROM users ON (id) VALUES (5) RETURNING id", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.DeleteStmt{ + Relations: &ast.List{Items: []ast.Node{&ast.RangeVar{Relname: strPtr("users")}}}, + OnCols: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{Name: strPtr("id")}, + }, + }, + OnSelectStmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + ValuesLists: &ast.List{ + Items: []ast.Node{ + &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.Integer{Ival: 5}}, + }, + }, + }, + }, + FromClause: &ast.List{}, + TargetList: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + Batch: false, + WhereClause: nil, + }, + }, + }, + }, + { + stmt: "DELETE FROM users ON (id) SELECT 1 AS id RETURNING id", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.DeleteStmt{ + Relations: &ast.List{Items: []ast.Node{&ast.RangeVar{Relname: strPtr("users")}}}, + OnCols: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{Name: strPtr("id")}, + }, + }, + OnSelectStmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Name: strPtr("id"), + Val: &ast.A_Const{Val: &ast.Integer{Ival: 1}}, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "id"}}}, + }, + }, + }, + }, + Batch: false, + WhereClause: nil, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + cmpopts.IgnoreFields(ast.ResTarget{}, "Location"), + cmpopts.IgnoreFields(ast.ColumnRef{}, "Location"), + cmpopts.IgnoreFields(ast.A_Expr{}, "Location"), + cmpopts.IgnoreFields(ast.RangeVar{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/drop_role_test.go b/internal/engine/ydb/catalog_tests/drop_role_test.go new file mode 100644 index 0000000000..224be53ed1 --- /dev/null +++ b/internal/engine/ydb/catalog_tests/drop_role_test.go @@ -0,0 +1,87 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestDropRole(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: `DROP USER user1;`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.DropRoleStmt{ + MissingOk: false, + Roles: &ast.List{ + Items: []ast.Node{ + &ast.RoleSpec{Rolename: strPtr("user1"), Roletype: ast.RoleSpecType(1)}, + }, + }, + }, + }, + }, + }, + { + stmt: "DROP USER IF EXISTS admin, user2", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.DropRoleStmt{ + MissingOk: true, + Roles: &ast.List{ + Items: []ast.Node{ + &ast.RoleSpec{Rolename: strPtr("admin"), Roletype: ast.RoleSpecType(1)}, + &ast.RoleSpec{Rolename: strPtr("user2"), Roletype: ast.RoleSpecType(1)}, + }, + }, + }, + }, + }, + }, + { + stmt: "DROP GROUP team1, team2", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.DropRoleStmt{ + MissingOk: false, + Roles: &ast.List{ + Items: []ast.Node{ + &ast.RoleSpec{Rolename: strPtr("team1"), Roletype: ast.RoleSpecType(1)}, + &ast.RoleSpec{Rolename: strPtr("team2"), Roletype: ast.RoleSpecType(1)}, + }, + }, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.RoleSpec{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/insert_test.go b/internal/engine/ydb/catalog_tests/insert_test.go new file mode 100644 index 0000000000..c60d0920da --- /dev/null +++ b/internal/engine/ydb/catalog_tests/insert_test.go @@ -0,0 +1,161 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestInsert(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: "INSERT INTO users (id, name) VALUES (1, 'Alice') RETURNING *", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.InsertStmt{ + Relation: &ast.RangeVar{Relname: strPtr("users")}, + Cols: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{Name: strPtr("id")}, + &ast.ResTarget{Name: strPtr("name")}, + }, + }, + SelectStmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + ValuesLists: &ast.List{ + Items: []ast.Node{ + &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.Integer{Ival: 1}}, + &ast.A_Const{Val: &ast.String{Str: "Alice"}}, + }, + }, + }, + }, + TargetList: &ast.List{}, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + OnConflictClause: &ast.OnConflictClause{}, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.A_Star{}}}, + }, + }, + }, + }, + }, + }, + }, + }, + { + stmt: "INSERT OR IGNORE INTO users (id) VALUES (3) RETURNING id, name", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.InsertStmt{ + Relation: &ast.RangeVar{Relname: strPtr("users")}, + Cols: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{Name: strPtr("id")}, + }, + }, + SelectStmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + ValuesLists: &ast.List{ + Items: []ast.Node{ + &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.Integer{Ival: 3}}, + }, + }, + }, + }, + TargetList: &ast.List{}, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + OnConflictClause: &ast.OnConflictClause{ + Action: ast.OnConflictAction_INSERT_OR_IGNORE, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "id"}}}}, + }, + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "name"}}}}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: "UPSERT INTO users (id) VALUES (4) RETURNING id", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.InsertStmt{ + Relation: &ast.RangeVar{Relname: strPtr("users")}, + Cols: &ast.List{Items: []ast.Node{&ast.ResTarget{Name: strPtr("id")}}}, + SelectStmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + ValuesLists: &ast.List{Items: []ast.Node{&ast.List{Items: []ast.Node{&ast.A_Const{Val: &ast.Integer{Ival: 4}}}}}}, + TargetList: &ast.List{}, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + OnConflictClause: &ast.OnConflictClause{Action: ast.OnConflictAction_UPSERT}, + ReturningList: &ast.List{Items: []ast.Node{&ast.ResTarget{Val: &ast.ColumnRef{Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "id"}}}}, Indirection: &ast.List{}}}}, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + cmpopts.IgnoreFields(ast.ResTarget{}, "Location"), + cmpopts.IgnoreFields(ast.ColumnRef{}, "Location"), + cmpopts.IgnoreFields(ast.A_Expr{}, "Location"), + cmpopts.IgnoreFields(ast.RangeVar{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/pragma_test.go b/internal/engine/ydb/catalog_tests/pragma_test.go new file mode 100644 index 0000000000..fef5f76f88 --- /dev/null +++ b/internal/engine/ydb/catalog_tests/pragma_test.go @@ -0,0 +1,118 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestPragma(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: `PRAGMA AutoCommit`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.Pragma_stmt{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.String{Str: "autocommit"}}, + }, + }, + }, + }, + }, + }, + { + stmt: `PRAGMA TablePathPrefix = "home/yql"`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.Pragma_stmt{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.String{Str: "tablepathprefix"}}, + }, + }, + Equals: true, + Values: &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.String{Str: "home/yql"}}, + }, + }, + }, + }, + }, + }, + { + stmt: `PRAGMA Warning("disable", "1101")`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.Pragma_stmt{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.String{Str: "warning"}}, + }, + }, + Equals: false, + Values: &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.String{Str: "disable"}}, + &ast.A_Const{Val: &ast.String{Str: "1101"}}, + }, + }, + }, + }, + }, + }, + { + stmt: `PRAGMA yson.AutoConvert = true`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.Pragma_stmt{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.String{Str: "yson"}}, + &ast.A_Const{Val: &ast.String{Str: "autoconvert"}}, + }, + }, + Equals: true, + Values: &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.Boolean{Boolval: true}}, + }, + }, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.Pragma_stmt{}, "Location"), + cmpopts.IgnoreFields(ast.ColumnRef{}, "Location"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/select_test.go b/internal/engine/ydb/catalog_tests/select_test.go new file mode 100644 index 0000000000..f01171f12a --- /dev/null +++ b/internal/engine/ydb/catalog_tests/select_test.go @@ -0,0 +1,745 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func strPtr(s string) *string { + return &s +} + +func TestSelect(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + // Basic Types Select + { + stmt: `SELECT 52`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.Integer{Ival: 52}, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: `SELECT 'hello'`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.String{Str: "hello"}, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: `SELECT 'it\'s string with quote in it'`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.String{Str: `it\'s string with quote in it`}, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: "SELECT 3.14", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.Float{Str: "3.14"}, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: "SELECT NULL", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.Null{}, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: "SELECT true", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.Boolean{Boolval: true}, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: "SELECT 2+3*4", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Expr{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "+"}, + }, + }, + Lexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 2}, + }, + Rexpr: &ast.A_Expr{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "*"}, + }, + }, + Lexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 3}, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 4}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + + // Select with From Clause tests + { + stmt: `SELECT * FROM users`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.A_Star{}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: "SELECT id AS identifier FROM users", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Name: strPtr("identifier"), + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: "SELECT a.b.c FROM table", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "a"}, + &ast.String{Str: "b"}, + &ast.String{Str: "c"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("table"), + }, + }, + }, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: "SELECT id.age, 3.14, 'abc', NULL, false FROM users", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + &ast.String{Str: "age"}, + }, + }, + }, + }, + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.Float{Str: "3.14"}, + }, + }, + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.String{Str: "abc"}, + }, + }, + &ast.ResTarget{ + Val: &ast.Null{}, + }, + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.Boolean{Boolval: false}, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: `SELECT id, name FROM users WHERE age > 30`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "name"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + WhereClause: &ast.A_Expr{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: ">"}, + }, + }, + Lexpr: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "age"}, + }, + }, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 30}, + }, + }, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + { + stmt: `(SELECT 1) UNION ALL (SELECT 2)`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{}, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + Op: ast.Union, + All: true, + Larg: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.Integer{Ival: 1}, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + Rarg: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.A_Const{ + Val: &ast.Integer{Ival: 2}, + }, + }, + }, + }, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + }, + { + stmt: `SELECT id FROM users ORDER BY id DESC`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + LockingClause: &ast.List{}, + SortClause: &ast.List{ + Items: []ast.Node{ + &ast.SortBy{ + Node: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + SortbyDir: ast.SortByDirDesc, + UseOp: &ast.List{}, + }, + }, + }, + }, + }, + }, + }, + { + stmt: `SELECT id FROM users LIMIT 10 OFFSET 5`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + LimitCount: &ast.A_Const{ + Val: &ast.Integer{Ival: 10}, + }, + LimitOffset: &ast.A_Const{ + Val: &ast.Integer{Ival: 5}, + }, + }, + }, + }, + }, + { + stmt: `SELECT id FROM users WHERE id > 10 GROUP BY id HAVING id > 10`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + GroupClause: &ast.List{ + Items: []ast.Node{ + &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + WhereClause: &ast.A_Expr{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: ">"}, + }, + }, + Lexpr: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 10}, + }, + }, + HavingClause: &ast.A_Expr{ + Name: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: ">"}, + }, + }, + Lexpr: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 10}, + }, + }, + }, + }, + }, + }, + { + stmt: `SELECT id FROM users GROUP BY ROLLUP (id)`, + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{ + Relname: strPtr("users"), + }, + }, + }, + GroupClause: &ast.List{ + Items: []ast.Node{ + &ast.GroupingSet{ + Kind: 1, // T_GroupingSet: ROLLUP + Content: &ast.List{ + Items: []ast.Node{ + &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + }, + }, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + }, + }, + }, + } + + p := ydb.NewParser() + + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + cmpopts.IgnoreFields(ast.ResTarget{}, "Location"), + cmpopts.IgnoreFields(ast.ColumnRef{}, "Location"), + cmpopts.IgnoreFields(ast.A_Expr{}, "Location"), + cmpopts.IgnoreFields(ast.RangeVar{}, "Location"), + cmpopts.IgnoreFields(ast.SortBy{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/catalog_tests/update_test.go b/internal/engine/ydb/catalog_tests/update_test.go new file mode 100644 index 0000000000..b7ebeb3d6a --- /dev/null +++ b/internal/engine/ydb/catalog_tests/update_test.go @@ -0,0 +1,190 @@ +package ydb_test + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sqlc-dev/sqlc/internal/engine/ydb" + "github.com/sqlc-dev/sqlc/internal/sql/ast" +) + +func TestUpdate(t *testing.T) { + tests := []struct { + stmt string + expected ast.Node + }{ + { + stmt: "UPDATE users SET name = 'Bob' WHERE id = 1 RETURNING id;", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.UpdateStmt{ + Relations: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{Relname: strPtr("users")}, + }, + }, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Name: strPtr("name"), + Val: &ast.A_Const{ + Val: &ast.String{Str: "Bob"}, + }, + }, + }, + }, + WhereClause: &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "="}}}, + Lexpr: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "id"}}}, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 1}, + }, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "id"}}}, + }, + }, + }, + }, + FromClause: &ast.List{}, + WithClause: nil, + Batch: false, + OnCols: nil, + OnSelectStmt: nil, + }, + }, + }, + }, + { + stmt: "BATCH UPDATE users SET name = 'Charlie' WHERE id = 2 RETURNING *", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.UpdateStmt{ + Relations: &ast.List{ + Items: []ast.Node{ + &ast.RangeVar{Relname: strPtr("users")}, + }, + }, + TargetList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Name: strPtr("name"), + Val: &ast.A_Const{Val: &ast.String{Str: "Charlie"}}, + }, + }, + }, + WhereClause: &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "="}}}, + Lexpr: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.String{Str: "id"}}}, + }, + Rexpr: &ast.A_Const{ + Val: &ast.Integer{Ival: 2}, + }, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.A_Star{}}}, + }, + }, + }, + }, + FromClause: &ast.List{}, + WithClause: nil, + Batch: true, + OnCols: nil, + OnSelectStmt: nil, + }, + }, + }, + }, + { + stmt: "UPDATE users ON (id) VALUES (5) RETURNING id", + expected: &ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: &ast.UpdateStmt{ + Relations: &ast.List{Items: []ast.Node{&ast.RangeVar{Relname: strPtr("users")}}}, + OnCols: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{Name: strPtr("id")}, + }, + }, + OnSelectStmt: &ast.SelectStmt{ + DistinctClause: &ast.List{}, + ValuesLists: &ast.List{ + Items: []ast.Node{ + &ast.List{ + Items: []ast.Node{ + &ast.A_Const{Val: &ast.Integer{Ival: 5}}, + }, + }, + }, + }, + FromClause: &ast.List{}, + TargetList: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + }, + ReturningList: &ast.List{ + Items: []ast.Node{ + &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{ + &ast.String{Str: "id"}, + }, + }, + }, + }, + }, + }, + FromClause: &ast.List{}, + WithClause: nil, + Batch: false, + TargetList: nil, + WhereClause: nil, + }, + }, + }, + }, + } + + p := ydb.NewParser() + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmts, err := p.Parse(strings.NewReader(tc.stmt)) + if err != nil { + t.Fatalf("Failed to parse query %q: %v", tc.stmt, err) + } + if len(stmts) == 0 { + t.Fatalf("Query %q was not parsed", tc.stmt) + } + + diff := cmp.Diff(tc.expected, &stmts[0], + cmpopts.IgnoreFields(ast.RawStmt{}, "StmtLocation", "StmtLen"), + cmpopts.IgnoreFields(ast.A_Const{}, "Location"), + cmpopts.IgnoreFields(ast.ResTarget{}, "Location"), + cmpopts.IgnoreFields(ast.ColumnRef{}, "Location"), + cmpopts.IgnoreFields(ast.A_Expr{}, "Location"), + cmpopts.IgnoreFields(ast.RangeVar{}, "Location"), + ) + if diff != "" { + t.Errorf("AST mismatch for %q (-expected +got):\n%s", tc.stmt, diff) + } + }) + } +} diff --git a/internal/engine/ydb/convert.go b/internal/engine/ydb/convert.go new file mode 100755 index 0000000000..0fa339fa56 --- /dev/null +++ b/internal/engine/ydb/convert.go @@ -0,0 +1,3180 @@ +package ydb + +import ( + "fmt" + "log" + "strconv" + "strings" + + "github.com/antlr4-go/antlr/v4" + "github.com/sqlc-dev/sqlc/internal/debug" + "github.com/sqlc-dev/sqlc/internal/sql/ast" + parser "github.com/ydb-platform/yql-parsers/go" +) + +type cc struct { + parser.BaseYQLVisitor + content string +} + +func (c *cc) pos(token antlr.Token) int { + if token == nil { + return 0 + } + runeIdx := token.GetStart() + return byteOffsetFromRuneIndex(c.content, runeIdx) +} + +type node interface { + GetParser() antlr.Parser +} + +func todo(funcname string, n node) *ast.TODO { + if debug.Active { + log.Printf("ydb.%s: Unknown node type %T\n", funcname, n) + } + return &ast.TODO{} +} + +func identifier(id string) string { + if len(id) >= 2 && id[0] == '"' && id[len(id)-1] == '"' { + unquoted, _ := strconv.Unquote(id) + return unquoted + } + return strings.ToLower(id) +} + +func stripQuotes(s string) string { + if len(s) >= 2 && (s[0] == '\'' || s[0] == '"') && s[0] == s[len(s)-1] { + return s[1 : len(s)-1] + } + return s +} + +func NewIdentifier(t string) *ast.String { + return &ast.String{Str: identifier(t)} +} + +func (c *cc) VisitDrop_role_stmt(n *parser.Drop_role_stmtContext) interface{} { + if n.DROP() == nil || (n.USER() == nil && n.GROUP() == nil) || len(n.AllRole_name()) == 0 { + return todo("VisitDrop_role_stmt", n) + } + + stmt := &ast.DropRoleStmt{ + MissingOk: n.IF() != nil && n.EXISTS() != nil, + Roles: &ast.List{}, + } + + for _, role := range n.AllRole_name() { + member, isParam, _ := c.extractRoleSpec(role, ast.RoleSpecType(1)) + if member == nil { + return todo("VisitDrop_role_stmt", role) + } + + if debug.Active && isParam { + log.Printf("YDB does not currently support parameters in the DROP ROLE statement") + } + + stmt.Roles.Items = append(stmt.Roles.Items, member) + } + + return stmt +} + +func (c *cc) VisitAlter_group_stmt(n *parser.Alter_group_stmtContext) interface{} { + if n.ALTER() == nil || n.GROUP() == nil || len(n.AllRole_name()) == 0 { + return todo("VisitAlter_group_stmt", n) + } + role, paramFlag, _ := c.extractRoleSpec(n.Role_name(0), ast.RoleSpecType(1)) + if role == nil { + return todo("VisitAlter_group_stmt", n) + } + + if debug.Active && paramFlag { + log.Printf("YDB does not currently support parameters in the ALTER GROUP statement") + } + + stmt := &ast.AlterRoleStmt{ + Role: role, + Action: 1, + Options: &ast.List{}, + } + + switch { + case n.RENAME() != nil && n.TO() != nil && len(n.AllRole_name()) > 1: + newName, ok := n.Role_name(1).Accept(c).(ast.Node) + if !ok { + return todo("VisitAlter_group_stmt", n.Role_name(1)) + } + action := "rename" + + defElem := &ast.DefElem{ + Defname: &action, + Defaction: ast.DefElemAction(1), + Location: c.pos(n.Role_name(1).GetStart()), + } + + bindFlag := true + switch v := newName.(type) { + case *ast.A_Const: + switch val := v.Val.(type) { + case *ast.String: + bindFlag = false + defElem.Arg = val + case *ast.Boolean: + defElem.Arg = val + default: + return todo("VisitAlter_group_stmt", n.Role_name(1)) + } + case *ast.ParamRef, *ast.A_Expr: + defElem.Arg = newName + default: + return todo("VisitAlter_group_stmt", n.Role_name(1)) + } + + if debug.Active && !paramFlag && bindFlag { + log.Printf("YDB does not currently support parameters in the ALTER GROUP statement") + } + + stmt.Options.Items = append(stmt.Options.Items, defElem) + + case (n.ADD() != nil || n.DROP() != nil) && len(n.AllRole_name()) > 1: + defname := "rolemembers" + optionList := &ast.List{} + for _, role := range n.AllRole_name()[1:] { + member, isParam, _ := c.extractRoleSpec(role, ast.RoleSpecType(1)) + if member == nil { + return todo("VisitAlter_group_stmt", role) + } + + if debug.Active && isParam && !paramFlag { + log.Printf("YDB does not currently support parameters in the ALTER GROUP statement") + } + + optionList.Items = append(optionList.Items, member) + } + + var action ast.DefElemAction + if n.ADD() != nil { + action = 3 + } else { + action = 4 + } + + stmt.Options.Items = append(stmt.Options.Items, &ast.DefElem{ + Defname: &defname, + Arg: optionList, + Defaction: action, + Location: c.pos(n.Role_name(1).GetStart()), + }) + } + + return stmt +} + +func (c *cc) VisitAlter_user_stmt(n *parser.Alter_user_stmtContext) interface{} { + if n.ALTER() == nil || n.USER() == nil || len(n.AllRole_name()) == 0 { + return todo("VisitAlter_user_stmt", n) + } + + role, paramFlag, _ := c.extractRoleSpec(n.Role_name(0), ast.RoleSpecType(1)) + if role == nil { + return todo("VisitAlter_group_stmt", n) + } + + if debug.Active && paramFlag { + log.Printf("YDB does not currently support parameters in the ALTER USER statement") + } + + stmt := &ast.AlterRoleStmt{ + Role: role, + Action: 1, + Options: &ast.List{}, + } + + switch { + case n.RENAME() != nil && n.TO() != nil && len(n.AllRole_name()) > 1: + newName, ok := n.Role_name(1).Accept(c).(ast.Node) + if !ok { + return todo("VisitAlter_user_stmt", n.Role_name(1)) + } + action := "rename" + + defElem := &ast.DefElem{ + Defname: &action, + Defaction: ast.DefElemAction(1), + Location: c.pos(n.Role_name(1).GetStart()), + } + + bindFlag := true + switch v := newName.(type) { + case *ast.A_Const: + switch val := v.Val.(type) { + case *ast.String: + bindFlag = false + defElem.Arg = val + case *ast.Boolean: + defElem.Arg = val + default: + return todo("VisitAlter_user_stmt", n.Role_name(1)) + } + case *ast.ParamRef, *ast.A_Expr: + defElem.Arg = newName + default: + return todo("VisitAlter_user_stmt", n.Role_name(1)) + } + + if debug.Active && !paramFlag && bindFlag { + log.Printf("YDB does not currently support parameters in the ALTER USER statement") + } + + stmt.Options.Items = append(stmt.Options.Items, defElem) + + case len(n.AllUser_option()) > 0: + for _, opt := range n.AllUser_option() { + if temp := opt.Accept(c); temp != nil { + var node, ok = temp.(ast.Node) + if !ok { + return todo("VisitAlter_user_stmt", opt) + } + stmt.Options.Items = append(stmt.Options.Items, node) + } + } + } + + return stmt +} + +func (c *cc) VisitCreate_group_stmt(n *parser.Create_group_stmtContext) interface{} { + if n.CREATE() == nil || n.GROUP() == nil || len(n.AllRole_name()) == 0 { + return todo("VisitCreate_group_stmt", n) + } + groupName, ok := n.Role_name(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitCreate_group_stmt", n.Role_name(0)) + } + + stmt := &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(3), + Options: &ast.List{}, + } + + paramFlag := true + switch v := groupName.(type) { + case *ast.A_Const: + switch val := v.Val.(type) { + case *ast.String: + paramFlag = false + stmt.Role = &val.Str + case *ast.Boolean: + stmt.BindRole = groupName + default: + return todo("VisitCreate_group_stmt", n.Role_name(0)) + } + case *ast.ParamRef, *ast.A_Expr: + stmt.BindRole = groupName + default: + return todo("VisitCreate_group_stmt", n.Role_name(0)) + } + + if debug.Active && paramFlag { + log.Printf("YDB does not currently support parameters in the CREATE GROUP statement") + } + + if n.WITH() != nil && n.USER() != nil && len(n.AllRole_name()) > 1 { + defname := "rolemembers" + optionList := &ast.List{} + for _, role := range n.AllRole_name()[1:] { + member, isParam, _ := c.extractRoleSpec(role, ast.RoleSpecType(1)) + if member == nil { + return todo("VisitCreate_group_stmt", role) + } + + if debug.Active && isParam && !paramFlag { + log.Printf("YDB does not currently support parameters in the CREATE GROUP statement") + } + + optionList.Items = append(optionList.Items, member) + } + + stmt.Options.Items = append(stmt.Options.Items, &ast.DefElem{ + Defname: &defname, + Arg: optionList, + Location: c.pos(n.Role_name(1).GetStart()), + }) + } + + return stmt +} + +func (c *cc) VisitUse_stmt(n *parser.Use_stmtContext) interface{} { + if n.USE() != nil && n.Cluster_expr() != nil { + clusterExpr, ok := n.Cluster_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitUse_stmt", n.Cluster_expr()) + } + stmt := &ast.UseStmt{ + Xpr: clusterExpr, + Location: c.pos(n.Cluster_expr().GetStart()), + } + return stmt + } + return todo("VisitUse_stmt", n) +} + +func (c *cc) VisitCluster_expr(n *parser.Cluster_exprContext) interface{} { + var node ast.Node + + switch { + case n.Pure_column_or_named() != nil: + pureCtx := n.Pure_column_or_named() + if anID := pureCtx.An_id(); anID != nil { + name := parseAnId(anID) + node = &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{NewIdentifier(name)}}, + Location: c.pos(anID.GetStart()), + } + } else if bp := pureCtx.Bind_parameter(); bp != nil { + temp, ok := bp.Accept(c).(ast.Node) + if !ok { + return todo("VisitCluster_expr", bp) + } + node = temp + } + case n.ASTERISK() != nil: + node = &ast.A_Star{} + default: + return todo("VisitCluster_expr", n) + } + + if n.An_id() != nil && n.COLON() != nil { + name := parseAnId(n.An_id()) + return &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: ":"}}}, + Lexpr: &ast.String{Str: name}, + Rexpr: node, + Location: c.pos(n.GetStart()), + } + } + + return node +} + +func (c *cc) VisitCreate_user_stmt(n *parser.Create_user_stmtContext) interface{} { + if n.CREATE() == nil || n.USER() == nil || n.Role_name() == nil { + return todo("VisitCreate_user_stmt", n) + } + roleNode, ok := n.Role_name().Accept(c).(ast.Node) + if !ok { + return todo("VisitCreate_user_stmt", n.Role_name()) + } + + stmt := &ast.CreateRoleStmt{ + StmtType: ast.RoleStmtType(2), + Options: &ast.List{}, + } + + paramFlag := true + switch v := roleNode.(type) { + case *ast.A_Const: + switch val := v.Val.(type) { + case *ast.String: + paramFlag = false + stmt.Role = &val.Str + case *ast.Boolean: + stmt.BindRole = roleNode + default: + return todo("VisitCreate_user_stmt", n.Role_name()) + } + case *ast.ParamRef, *ast.A_Expr: + stmt.BindRole = roleNode + default: + return todo("VisitCreate_user_stmt", n.Role_name()) + } + + if debug.Active && paramFlag { + log.Printf("YDB does not currently support parameters in the CREATE USER statement") + } + + if len(n.AllUser_option()) > 0 { + options := []ast.Node{} + for _, opt := range n.AllUser_option() { + if temp := opt.Accept(c); temp != nil { + node, ok := temp.(ast.Node) + if !ok { + return todo("VisitCreate_user_stmt", opt) + } + options = append(options, node) + } + } + if len(options) > 0 { + stmt.Options = &ast.List{Items: options} + } + } + return stmt +} + +func (c *cc) VisitUser_option(n *parser.User_optionContext) interface{} { + switch { + case n.Authentication_option() != nil: + aOpt := n.Authentication_option() + if pOpt := aOpt.Password_option(); pOpt != nil { + if pOpt.PASSWORD() != nil { + name := "password" + pValue := pOpt.Password_value() + var password ast.Node + if pValue.STRING_VALUE() != nil { + password = &ast.String{Str: stripQuotes(pValue.STRING_VALUE().GetText())} + } else { + password = &ast.Null{} + } + return &ast.DefElem{ + Defname: &name, + Arg: password, + Location: c.pos(pOpt.GetStart()), + } + } + } else if hOpt := aOpt.Hash_option(); hOpt != nil { + if debug.Active { + log.Printf("YDB does not currently support HASH in CREATE USER statement") + } + var pass string + if hOpt.HASH() != nil && hOpt.STRING_VALUE() != nil { + pass = stripQuotes(hOpt.STRING_VALUE().GetText()) + } + name := "hash" + return &ast.DefElem{ + Defname: &name, + Arg: &ast.String{Str: pass}, + Location: c.pos(hOpt.GetStart()), + } + } + + case n.Login_option() != nil: + lOpt := n.Login_option() + var name string + if lOpt.LOGIN() != nil { + name = "login" + } else if lOpt.NOLOGIN() != nil { + name = "nologin" + } + return &ast.DefElem{ + Defname: &name, + Arg: &ast.Boolean{Boolval: lOpt.LOGIN() != nil}, + Location: c.pos(lOpt.GetStart()), + } + default: + return todo("VisitUser_option", n) + } + return todo("VisitUser_option", n) +} + +func (c *cc) VisitRole_name(n *parser.Role_nameContext) interface{} { + switch { + case n.An_id_or_type() != nil: + name := parseAnIdOrType(n.An_id_or_type()) + return &ast.A_Const{Val: NewIdentifier(name), Location: c.pos(n.An_id_or_type().GetStart())} + case n.Bind_parameter() != nil: + bindPar, ok := n.Bind_parameter().Accept(c).(ast.Node) + if !ok { + return todo("VisitRole_name", n.Bind_parameter()) + } + return bindPar + } + return todo("VisitRole_name", n) +} + +func (c *cc) VisitCommit_stmt(n *parser.Commit_stmtContext) interface{} { + if n.COMMIT() != nil { + return &ast.TransactionStmt{Kind: ast.TransactionStmtKind(3)} + } + return todo("VisitCommit_stmt", n) +} + +func (c *cc) VisitRollback_stmt(n *parser.Rollback_stmtContext) interface{} { + if n.ROLLBACK() != nil { + return &ast.TransactionStmt{Kind: ast.TransactionStmtKind(4)} + } + return todo("VisitRollback_stmt", n) +} + +func (c *cc) VisitAlter_table_stmt(n *parser.Alter_table_stmtContext) interface{} { + if n.ALTER() == nil || n.TABLE() == nil || n.Simple_table_ref() == nil || len(n.AllAlter_table_action()) == 0 { + return todo("VisitAlter_table_stmt", n) + } + + stmt := &ast.AlterTableStmt{ + Table: parseTableName(n.Simple_table_ref().Simple_table_ref_core()), + Cmds: &ast.List{}, + } + + for _, action := range n.AllAlter_table_action() { + if action == nil { + continue + } + + switch { + case action.Alter_table_add_column() != nil: + ac := action.Alter_table_add_column() + if ac.ADD() != nil && ac.Column_schema() != nil { + temp, ok := ac.Column_schema().Accept(c).(ast.Node) + if !ok { + return todo("VisitAlter_table_stmt", ac.Column_schema()) + } + columnDef, ok := temp.(*ast.ColumnDef) + if !ok { + return todo("VisitAlter_table_stmt", ac.Column_schema()) + } + stmt.Cmds.Items = append(stmt.Cmds.Items, &ast.AlterTableCmd{ + Name: &columnDef.Colname, + Subtype: ast.AT_AddColumn, + Def: columnDef, + }) + } + case action.Alter_table_drop_column() != nil: + ac := action.Alter_table_drop_column() + if ac.DROP() != nil && ac.An_id() != nil { + name := parseAnId(ac.An_id()) + stmt.Cmds.Items = append(stmt.Cmds.Items, &ast.AlterTableCmd{ + Name: &name, + Subtype: ast.AT_DropColumn, + }) + } + case action.Alter_table_alter_column_drop_not_null() != nil: + ac := action.Alter_table_alter_column_drop_not_null() + if ac.DROP() != nil && ac.NOT() != nil && ac.NULL() != nil && ac.An_id() != nil { + name := parseAnId(ac.An_id()) + stmt.Cmds.Items = append(stmt.Cmds.Items, &ast.AlterTableCmd{ + Name: &name, + Subtype: ast.AT_DropNotNull, + }) + } + case action.Alter_table_rename_to() != nil: + ac := action.Alter_table_rename_to() + if ac.RENAME() != nil && ac.TO() != nil && ac.An_id_table() != nil { + // FIXME: Returning here may be incorrect if there are multiple specs + newName := parseAnIdTable(ac.An_id_table()) + return &ast.RenameTableStmt{ + Table: stmt.Table, + NewName: &newName, + } + } + case action.Alter_table_add_index() != nil, + action.Alter_table_drop_index() != nil, + action.Alter_table_add_column_family() != nil, + action.Alter_table_alter_column_family() != nil, + action.Alter_table_set_table_setting_uncompat() != nil, + action.Alter_table_set_table_setting_compat() != nil, + action.Alter_table_reset_table_setting() != nil, + action.Alter_table_add_changefeed() != nil, + action.Alter_table_alter_changefeed() != nil, + action.Alter_table_drop_changefeed() != nil, + action.Alter_table_rename_index_to() != nil, + action.Alter_table_alter_index() != nil: + // All these actions do not change column schema relevant to sqlc; no-op. + // Intentionally ignored. + } + } + + return stmt +} + +func (c *cc) VisitDo_stmt(n *parser.Do_stmtContext) interface{} { + if n.DO() == nil || (n.Call_action() == nil && n.Inline_action() == nil) { + return todo("VisitDo_stmt", n) + } + + switch { + case n.Call_action() != nil: + result, ok := n.Call_action().Accept(c).(ast.Node) + if !ok { + return todo("VisitDo_stmt", n.Call_action()) + } + return result + + case n.Inline_action() != nil: + result, ok := n.Inline_action().Accept(c).(ast.Node) + if !ok { + return todo("VisitDo_stmt", n.Inline_action()) + } + return result + } + + return todo("VisitDo_stmt", n) +} + +func (c *cc) VisitCall_action(n *parser.Call_actionContext) interface{} { + if n == nil { + return todo("VisitCall_action", n) + } + if n.LPAREN() != nil && n.RPAREN() != nil { + funcCall := &ast.FuncCall{ + Funcname: &ast.List{}, + Args: &ast.List{}, + AggOrder: &ast.List{}, + } + + if n.Bind_parameter() != nil { + bindPar, ok := n.Bind_parameter().Accept(c).(ast.Node) + if !ok { + return todo("VisitCall_action", n.Bind_parameter()) + } + funcCall.Funcname.Items = append(funcCall.Funcname.Items, bindPar) + } else if n.EMPTY_ACTION() != nil { + funcCall.Funcname.Items = append(funcCall.Funcname.Items, &ast.String{Str: "EMPTY_ACTION"}) + } + + if n.Expr_list() != nil { + for _, expr := range n.Expr_list().AllExpr() { + exprNode, ok := expr.Accept(c).(ast.Node) + if !ok { + return todo("VisitCall_action", expr) + } + funcCall.Args.Items = append(funcCall.Args.Items, exprNode) + } + } + + return &ast.DoStmt{ + Args: &ast.List{Items: []ast.Node{funcCall}}, + } + } + return todo("VisitCall_action", n) +} + +func (c *cc) VisitInline_action(n *parser.Inline_actionContext) interface{} { + if n == nil { + return todo("VisitInline_action", n) + } + if n.BEGIN() != nil && n.END() != nil && n.DO() != nil { + args := &ast.List{} + if defineBody := n.Define_action_or_subquery_body(); defineBody != nil { + cores := defineBody.AllSql_stmt_core() + for _, stmtCore := range cores { + if converted := stmtCore.Accept(c); converted != nil { + var convertedNode, ok = converted.(ast.Node) + if !ok { + return todo("VisitInline_action", stmtCore) + } + args.Items = append(args.Items, convertedNode) + } + } + } + return &ast.DoStmt{Args: args} + } + return todo("VisitInline_action", n) +} + +func (c *cc) VisitDrop_table_stmt(n *parser.Drop_table_stmtContext) interface{} { + if n.DROP() != nil && (n.TABLESTORE() != nil || (n.EXTERNAL() != nil && n.TABLE() != nil) || n.TABLE() != nil) { + name := parseTableName(n.Simple_table_ref().Simple_table_ref_core()) + stmt := &ast.DropTableStmt{ + IfExists: n.IF() != nil && n.EXISTS() != nil, + Tables: []*ast.TableName{name}, + } + return stmt + } + return todo("VisitDrop_table_stmt", n) +} + +func (c *cc) VisitDelete_stmt(n *parser.Delete_stmtContext) interface{} { + batch := n.BATCH() != nil + + tableName := identifier(n.Simple_table_ref().Simple_table_ref_core().GetText()) + rel := &ast.RangeVar{Relname: &tableName} + + var where ast.Node + if n.WHERE() != nil && n.Expr() != nil { + whereNode, ok := n.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitDelete_stmt", n.Expr()) + } + where = whereNode + } + var cols *ast.List + var source ast.Node + if n.ON() != nil && n.Into_values_source() != nil { + nVal := n.Into_values_source() + // todo: handle default values when implemented + if pureCols := nVal.Pure_column_list(); pureCols != nil { + cols = &ast.List{} + for _, anID := range pureCols.AllAn_id() { + name := identifier(parseAnId(anID)) + cols.Items = append(cols.Items, &ast.ResTarget{ + Name: &name, + Location: c.pos(anID.GetStart()), + }) + } + } + + valSource := nVal.Values_source() + if valSource != nil { + switch { + case valSource.Values_stmt() != nil: + stmt := emptySelectStmt() + temp, ok := valSource.Values_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitDelete_stmt", valSource.Values_stmt()) + } + list, ok := temp.(*ast.List) + if !ok { + return todo("VisitDelete_stmt", valSource.Values_stmt()) + } + stmt.ValuesLists = list + source = stmt + case valSource.Select_stmt() != nil: + temp, ok := valSource.Select_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitDelete_stmt", valSource.Select_stmt()) + } + source = temp + } + } + } + + returning := &ast.List{} + if ret := n.Returning_columns_list(); ret != nil { + temp, ok := ret.Accept(c).(ast.Node) + if !ok { + return todo("VisitDelete_stmt", n.Returning_columns_list()) + } + returningNode, ok := temp.(*ast.List) + if !ok { + return todo("VisitDelete_stmt", n.Returning_columns_list()) + } + returning = returningNode + } + + stmts := &ast.DeleteStmt{ + Relations: &ast.List{Items: []ast.Node{rel}}, + WhereClause: where, + ReturningList: returning, + Batch: batch, + OnCols: cols, + OnSelectStmt: source, + } + + return stmts +} + +func (c *cc) VisitPragma_stmt(n *parser.Pragma_stmtContext) interface{} { + if n.PRAGMA() != nil && n.An_id() != nil { + prefix := "" + if p := n.Opt_id_prefix_or_type(); p != nil { + prefix = parseAnIdOrType(p.An_id_or_type()) + } + items := []ast.Node{} + if prefix != "" { + items = append(items, &ast.A_Const{Val: NewIdentifier(prefix)}) + } + + name := parseAnId(n.An_id()) + items = append(items, &ast.A_Const{Val: NewIdentifier(name)}) + + stmt := &ast.Pragma_stmt{ + Name: &ast.List{Items: items}, + Location: c.pos(n.An_id().GetStart()), + } + + if n.EQUALS() != nil { + stmt.Equals = true + if val := n.Pragma_value(0); val != nil { + valNode, ok := val.Accept(c).(ast.Node) + if !ok { + return todo("VisitPragma_stmt", n.Pragma_value(0)) + } + stmt.Values = &ast.List{Items: []ast.Node{valNode}} + } + } else if lp := n.LPAREN(); lp != nil { + values := []ast.Node{} + for _, v := range n.AllPragma_value() { + valNode, ok := v.Accept(c).(ast.Node) + if !ok { + return todo("VisitPragma_stmt", v) + } + values = append(values, valNode) + } + stmt.Values = &ast.List{Items: values} + } + + return stmt + } + return todo("VisitPragma_stmt", n) +} + +func (c *cc) VisitPragma_value(n *parser.Pragma_valueContext) interface{} { + switch { + case n.Signed_number() != nil: + if n.Signed_number().Integer() != nil { + text := n.Signed_number().GetText() + val, err := parseIntegerValue(text) + if err != nil { + if debug.Active { + log.Printf("Failed to parse integer value '%s': %v", text, err) + } + return &ast.TODO{} + } + return &ast.A_Const{Val: &ast.Integer{Ival: val}, Location: c.pos(n.GetStart())} + } + if n.Signed_number().Real_() != nil { + text := n.Signed_number().GetText() + return &ast.A_Const{Val: &ast.Float{Str: text}, Location: c.pos(n.GetStart())} + } + case n.STRING_VALUE() != nil: + val := n.STRING_VALUE().GetText() + if len(val) >= 2 { + val = val[1 : len(val)-1] + } + return &ast.A_Const{Val: &ast.String{Str: val}, Location: c.pos(n.GetStart())} + case n.Bool_value() != nil: + var i bool + if n.Bool_value().TRUE() != nil { + i = true + } + return &ast.A_Const{Val: &ast.Boolean{Boolval: i}, Location: c.pos(n.GetStart())} + case n.Bind_parameter() != nil: + bindPar := n.Bind_parameter().Accept(c) + var bindParNode, ok = bindPar.(ast.Node) + if !ok { + return todo("VisitPragma_value", n.Bind_parameter()) + } + return bindParNode + } + + return todo("VisitPragma_value", n) +} + +func (c *cc) VisitUpdate_stmt(n *parser.Update_stmtContext) interface{} { + if n == nil || n.UPDATE() == nil { + return todo("VisitUpdate_stmt", n) + } + batch := n.BATCH() != nil + + tableName := identifier(n.Simple_table_ref().Simple_table_ref_core().GetText()) + rel := &ast.RangeVar{Relname: &tableName} + + var where ast.Node + var setList *ast.List + var cols *ast.List + var source ast.Node + + if n.SET() != nil && n.Set_clause_choice() != nil { + nSet := n.Set_clause_choice() + setList = &ast.List{Items: []ast.Node{}} + + switch { + case nSet.Set_clause_list() != nil: + for _, clause := range nSet.Set_clause_list().AllSet_clause() { + targetCtx := clause.Set_target() + columnName := identifier(targetCtx.Column_name().GetText()) + expr, ok := clause.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitUpdate_stmt", clause.Expr()) + } + resTarget := &ast.ResTarget{ + Name: &columnName, + Val: expr, + Location: c.pos(clause.Expr().GetStart()), + } + setList.Items = append(setList.Items, resTarget) + } + + case nSet.Multiple_column_assignment() != nil: + multiAssign := nSet.Multiple_column_assignment() + targetsCtx := multiAssign.Set_target_list() + valuesCtx := multiAssign.Simple_values_source() + + var colNames []string + for _, target := range targetsCtx.AllSet_target() { + targetCtx := target.(*parser.Set_targetContext) + colNames = append(colNames, targetCtx.Column_name().GetText()) + } + + var rowExpr *ast.RowExpr + if exprList := valuesCtx.Expr_list(); exprList != nil { + rowExpr = &ast.RowExpr{ + Args: &ast.List{}, + } + for _, expr := range exprList.AllExpr() { + exprNode, ok := expr.Accept(c).(ast.Node) + if !ok { + return todo("VisitUpdate_stmt", expr) + } + rowExpr.Args.Items = append(rowExpr.Args.Items, exprNode) + } + } + + for i, colName := range colNames { + name := identifier(colName) + setList.Items = append(setList.Items, &ast.ResTarget{ + Name: &name, + Val: &ast.MultiAssignRef{ + Source: rowExpr, + Colno: i + 1, + Ncolumns: len(colNames), + }, + Location: c.pos(targetsCtx.Set_target(i).GetStart()), + }) + } + } + + if n.WHERE() != nil && n.Expr() != nil { + whereNode, ok := n.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitUpdate_stmt", n.Expr()) + } + where = whereNode + } + } else if n.ON() != nil && n.Into_values_source() != nil { + + // todo: handle default values when implemented + + nVal := n.Into_values_source() + + if pureCols := nVal.Pure_column_list(); pureCols != nil { + cols = &ast.List{} + for _, anID := range pureCols.AllAn_id() { + name := identifier(parseAnId(anID)) + cols.Items = append(cols.Items, &ast.ResTarget{ + Name: &name, + Location: c.pos(anID.GetStart()), + }) + } + } + + valSource := nVal.Values_source() + if valSource != nil { + switch { + case valSource.Values_stmt() != nil: + stmt := emptySelectStmt() + temp, ok := valSource.Values_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitUpdate_stmt", valSource.Values_stmt()) + } + list, ok := temp.(*ast.List) + if !ok { + return todo("VisitUpdate_stmt", valSource.Values_stmt()) + } + stmt.ValuesLists = list + source = stmt + case valSource.Select_stmt() != nil: + temp, ok := valSource.Select_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitUpdate_stmt", valSource.Select_stmt()) + } + source = temp + } + } + } + + returning := &ast.List{} + if ret := n.Returning_columns_list(); ret != nil { + temp, ok := ret.Accept(c).(ast.Node) + if !ok { + return todo("VisitDelete_stmt", n.Returning_columns_list()) + } + returningNode, ok := temp.(*ast.List) + if !ok { + return todo("VisitDelete_stmt", n.Returning_columns_list()) + } + returning = returningNode + } + + stmts := &ast.UpdateStmt{ + Relations: &ast.List{Items: []ast.Node{rel}}, + TargetList: setList, + WhereClause: where, + ReturningList: returning, + FromClause: &ast.List{}, + WithClause: nil, + Batch: batch, + OnCols: cols, + OnSelectStmt: source, + } + + return stmts +} + +func (c *cc) VisitInto_table_stmt(n *parser.Into_table_stmtContext) interface{} { + tableName := identifier(n.Into_simple_table_ref().Simple_table_ref().Simple_table_ref_core().GetText()) + rel := &ast.RangeVar{ + Relname: &tableName, + Location: c.pos(n.Into_simple_table_ref().GetStart()), + } + + onConflict := &ast.OnConflictClause{} + switch { + case n.INSERT() != nil && n.OR() != nil && n.ABORT() != nil: + onConflict.Action = ast.OnConflictAction_INSERT_OR_ABORT + case n.INSERT() != nil && n.OR() != nil && n.REVERT() != nil: + onConflict.Action = ast.OnConflictAction_INSERT_OR_REVERT + case n.INSERT() != nil && n.OR() != nil && n.IGNORE() != nil: + onConflict.Action = ast.OnConflictAction_INSERT_OR_IGNORE + case n.UPSERT() != nil: + onConflict.Action = ast.OnConflictAction_UPSERT + case n.REPLACE() != nil: + onConflict.Action = ast.OnConflictAction_REPLACE + } + + var cols *ast.List + var source ast.Node + if nVal := n.Into_values_source(); nVal != nil { + // todo: handle default values when implemented + + if pureCols := nVal.Pure_column_list(); pureCols != nil { + cols = &ast.List{} + for _, anID := range pureCols.AllAn_id() { + name := identifier(parseAnId(anID)) + cols.Items = append(cols.Items, &ast.ResTarget{ + Name: &name, + Location: c.pos(anID.GetStart()), + }) + } + } + + valSource := nVal.Values_source() + if valSource != nil { + switch { + case valSource.Values_stmt() != nil: + stmt := emptySelectStmt() + temp, ok := valSource.Values_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitInto_table_stmt", valSource.Values_stmt()) + } + stmtNode, ok := temp.(*ast.List) + if !ok { + return todo("VisitInto_table_stmt", valSource.Values_stmt()) + } + stmt.ValuesLists = stmtNode + source = stmt + case valSource.Select_stmt() != nil: + sourceNode, ok := valSource.Select_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitInto_table_stmt", valSource.Select_stmt()) + } + source = sourceNode + } + } + } + + returning := &ast.List{} + if ret := n.Returning_columns_list(); ret != nil { + temp, ok := ret.Accept(c).(ast.Node) + if !ok { + return todo("VisitInto_table_stmt", n.Returning_columns_list()) + } + returningNode, ok := temp.(*ast.List) + if !ok { + return todo("VisitInto_table_stmt", n.Returning_columns_list()) + } + returning = returningNode + } + + stmts := &ast.InsertStmt{ + Relation: rel, + Cols: cols, + SelectStmt: source, + OnConflictClause: onConflict, + ReturningList: returning, + } + + return stmts +} + +func (c *cc) VisitValues_stmt(n *parser.Values_stmtContext) interface{} { + mainList := &ast.List{} + + for _, rowCtx := range n.Values_source_row_list().AllValues_source_row() { + rowList := &ast.List{} + exprListCtx := rowCtx.Expr_list().(*parser.Expr_listContext) + + for _, exprCtx := range exprListCtx.AllExpr() { + if converted := exprCtx.Accept(c); converted != nil { + var convertedNode, ok = converted.(ast.Node) + if !ok { + return todo("VisitValues_stmt", exprCtx) + } + rowList.Items = append(rowList.Items, convertedNode) + } + } + + mainList.Items = append(mainList.Items, rowList) + + } + + return mainList +} + +func (c *cc) VisitReturning_columns_list(n *parser.Returning_columns_listContext) interface{} { + list := &ast.List{Items: []ast.Node{}} + + if n.ASTERISK() != nil { + target := &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{&ast.A_Star{}}}, + Location: c.pos(n.ASTERISK().GetSymbol()), + }, + Location: c.pos(n.ASTERISK().GetSymbol()), + } + list.Items = append(list.Items, target) + return list + } + + for _, idCtx := range n.AllAn_id() { + target := &ast.ResTarget{ + Indirection: &ast.List{}, + Val: &ast.ColumnRef{ + Fields: &ast.List{ + Items: []ast.Node{NewIdentifier(parseAnId(idCtx))}, + }, + Location: c.pos(idCtx.GetStart()), + }, + Location: c.pos(idCtx.GetStart()), + } + list.Items = append(list.Items, target) + } + + return list +} + +func (c *cc) VisitSelect_stmt(n *parser.Select_stmtContext) interface{} { + if len(n.AllSelect_kind_parenthesis()) == 0 { + return todo("VisitSelect_stmt", n) + } + + skp := n.Select_kind_parenthesis(0) + if skp == nil { + return todo("VisitSelect_stmt", skp) + } + + temp, ok := skp.Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_kind_parenthesis", skp) + } + left, ok := temp.(*ast.SelectStmt) + if left == nil || !ok { + return todo("VisitSelect_kind_parenthesis", skp) + } + + kinds := n.AllSelect_kind_parenthesis() + ops := n.AllSelect_op() + + for i := 1; i < len(kinds); i++ { + temp, ok := kinds[i].Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_kind_parenthesis", kinds[i]) + } + right, ok := temp.(*ast.SelectStmt) + if right == nil || !ok { + return todo("VisitSelect_kind_parenthesis", kinds[i]) + } + + var op ast.SetOperation + var all bool + if i-1 < len(ops) && ops[i-1] != nil { + so := ops[i-1] + switch { + case so.UNION() != nil: + op = ast.Union + case so.INTERSECT() != nil: + log.Fatalf("YDB: INTERSECT is not implemented yet") + case so.EXCEPT() != nil: + log.Fatalf("YDB: EXCEPT is not implemented yet") + default: + op = ast.None + } + all = so.ALL() != nil + } + larg := left + left = emptySelectStmt() + left.Op = op + left.All = all + left.Larg = larg + left.Rarg = right + } + + return left +} + +func (c *cc) VisitSelect_kind_parenthesis(n *parser.Select_kind_parenthesisContext) interface{} { + if n == nil || n.Select_kind_partial() == nil { + return todo("VisitSelect_kind_parenthesis", n) + } + partial := n.Select_kind_partial() + + sk := partial.Select_kind() + if sk == nil { + return todo("VisitSelect_kind_parenthesis", sk) + } + + var base ast.Node + switch { + case sk.Select_core() != nil: + baseNode, ok := sk.Select_core().Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_kind_parenthesis", sk.Select_core()) + } + base = baseNode + case sk.Process_core() != nil: + log.Fatalf("PROCESS is not supported in YDB engine") + case sk.Reduce_core() != nil: + log.Fatalf("REDUCE is not supported in YDB engine") + } + stmt, ok := base.(*ast.SelectStmt) + if !ok || stmt == nil { + return todo("VisitSelect_kind_parenthesis", sk.Select_core()) + } + + // TODO: handle INTO RESULT clause + + if partial.LIMIT() != nil { + exprs := partial.AllExpr() + if len(exprs) >= 1 { + temp, ok := exprs[0].Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_kind_parenthesis", exprs[0]) + } + stmt.LimitCount = temp + } + if partial.OFFSET() != nil { + if len(exprs) >= 2 { + temp, ok := exprs[1].Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_kind_parenthesis", exprs[1]) + } + stmt.LimitOffset = temp + } + } + } + + return stmt +} + +func (c *cc) VisitSelect_core(n *parser.Select_coreContext) interface{} { + stmt := emptySelectStmt() + if n.Opt_set_quantifier() != nil { + oq := n.Opt_set_quantifier() + if oq.DISTINCT() != nil { + stmt.DistinctClause.Items = append(stmt.DistinctClause.Items, &ast.TODO{}) // trick to handle distinct + } + } + resultCols := n.AllResult_column() + if len(resultCols) > 0 { + var items []ast.Node + for _, rc := range resultCols { + convNode, ok := rc.Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_core", rc) + } + items = append(items, convNode) + } + stmt.TargetList = &ast.List{ + Items: items, + } + } + + // TODO: handle WITHOUT clause + + jsList := n.AllJoin_source() + if len(n.AllFROM()) > 1 { + log.Fatalf("YDB: Only one FROM clause is allowed") + } + if len(jsList) > 0 { + var fromItems []ast.Node + for _, js := range jsList { + joinNode, ok := js.Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_core", js) + } + fromItems = append(fromItems, joinNode) + } + stmt.FromClause = &ast.List{ + Items: fromItems, + } + } + + exprIdx := 0 + if n.WHERE() != nil { + if whereCtx := n.Expr(exprIdx); whereCtx != nil { + where, ok := whereCtx.Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_core", whereCtx) + } + stmt.WhereClause = where + } + exprIdx++ + } + if n.HAVING() != nil { + if havingCtx := n.Expr(exprIdx); havingCtx != nil { + having, ok := havingCtx.Accept(c).(ast.Node) + if !ok || having == nil { + return todo("VisitSelect_core", havingCtx) + } + stmt.HavingClause = having + } + exprIdx++ + } + + if gbc := n.Group_by_clause(); gbc != nil { + if gel := gbc.Grouping_element_list(); gel != nil { + var groups []ast.Node + for _, ne := range gel.AllGrouping_element() { + groupBy, ok := ne.Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_core", ne) + } + groups = append(groups, groupBy) + } + if len(groups) > 0 { + stmt.GroupClause = &ast.List{Items: groups} + } + } + } + + if ext := n.Ext_order_by_clause(); ext != nil { + if ob := ext.Order_by_clause(); ob != nil && ob.ORDER() != nil && ob.BY() != nil { + // TODO: ASSUME ORDER BY + if sl := ob.Sort_specification_list(); sl != nil { + var orderItems []ast.Node + for _, sp := range sl.AllSort_specification() { + expr, ok := sp.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitSelect_core", sp.Expr()) + } + dir := ast.SortByDirDefault + if sp.ASC() != nil { + dir = ast.SortByDirAsc + } else if sp.DESC() != nil { + dir = ast.SortByDirDesc + } + orderItems = append(orderItems, &ast.SortBy{ + Node: expr, + SortbyDir: dir, + SortbyNulls: ast.SortByNullsUndefined, + UseOp: &ast.List{}, + Location: c.pos(sp.GetStart()), + }) + } + if len(orderItems) > 0 { + stmt.SortClause = &ast.List{Items: orderItems} + } + } + } + } + return stmt +} + +func (c *cc) VisitGrouping_element(n *parser.Grouping_elementContext) interface{} { + if n == nil { + return todo("VisitGrouping_element", n) + } + if ogs := n.Ordinary_grouping_set(); ogs != nil { + groupingSet, ok := ogs.Accept(c).(ast.Node) + if !ok { + return todo("VisitGrouping_element", ogs) + } + return groupingSet + } + if rl := n.Rollup_list(); rl != nil { + rollupList, ok := rl.Accept(c).(ast.Node) + if !ok { + return todo("VisitGrouping_element", rl) + } + return rollupList + } + if cl := n.Cube_list(); cl != nil { + cubeList, ok := cl.Accept(c).(ast.Node) + if !ok { + return todo("VisitGrouping_element", cl) + } + return cubeList + } + if gss := n.Grouping_sets_specification(); gss != nil { + groupingSets, ok := gss.Accept(c).(ast.Node) + if !ok { + return todo("VisitGrouping_element", gss) + } + return groupingSets + } + return todo("VisitGrouping_element", n) +} + +func (c *cc) VisitOrdinary_grouping_set(n *parser.Ordinary_grouping_setContext) interface{} { + if n == nil || n.Named_expr() == nil { + return todo("VisitOrdinary_grouping_set", n) + } + + namedExpr, ok := n.Named_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitOrdinary_grouping_set", n.Named_expr()) + } + return namedExpr +} + +func (c *cc) VisitRollup_list(n *parser.Rollup_listContext) interface{} { + if n == nil || n.ROLLUP() == nil || n.LPAREN() == nil || n.RPAREN() == nil { + return todo("VisitRollup_list", n) + } + + var items []ast.Node + if list := n.Ordinary_grouping_set_list(); list != nil { + for _, ogs := range list.AllOrdinary_grouping_set() { + og, ok := ogs.Accept(c).(ast.Node) + if !ok { + return todo("VisitRollup_list", ogs) + } + items = append(items, og) + } + } + return &ast.GroupingSet{Kind: 1, Content: &ast.List{Items: items}} +} + +func (c *cc) VisitCube_list(n *parser.Cube_listContext) interface{} { + if n == nil || n.CUBE() == nil || n.LPAREN() == nil || n.RPAREN() == nil { + return todo("VisitCube_list", n) + } + + var items []ast.Node + if list := n.Ordinary_grouping_set_list(); list != nil { + for _, ogs := range list.AllOrdinary_grouping_set() { + og, ok := ogs.Accept(c).(ast.Node) + if !ok { + return todo("VisitCube_list", ogs) + } + items = append(items, og) + } + } + + return &ast.GroupingSet{Kind: 2, Content: &ast.List{Items: items}} +} + +func (c *cc) VisitGrouping_sets_specification(n *parser.Grouping_sets_specificationContext) interface{} { + if n == nil || n.GROUPING() == nil || n.SETS() == nil || n.LPAREN() == nil || n.RPAREN() == nil { + return todo("VisitGrouping_sets_specification", n) + } + + var items []ast.Node + if gel := n.Grouping_element_list(); gel != nil { + for _, ge := range gel.AllGrouping_element() { + g, ok := ge.Accept(c).(ast.Node) + if !ok { + return todo("VisitGrouping_sets_specification", ge) + } + items = append(items, g) + } + } + return &ast.GroupingSet{Kind: 3, Content: &ast.List{Items: items}} +} + +func (c *cc) VisitResult_column(n *parser.Result_columnContext) interface{} { + // todo: support opt_id_prefix + target := &ast.ResTarget{ + Location: c.pos(n.GetStart()), + } + var val ast.Node + iexpr := n.Expr() + switch { + case n.ASTERISK() != nil: + val = c.convertWildCardField(n) + case iexpr != nil: + temp, ok := iexpr.Accept(c).(ast.Node) + if !ok { + return todo("VisitResult_column", iexpr) + } + val = temp + } + + if val == nil { + return todo("VisitResult_column", n) + } + switch { + case n.AS() != nil && n.An_id_or_type() != nil: + name := parseAnIdOrType(n.An_id_or_type()) + target.Name = &name + case n.An_id_as_compat() != nil: //nolint + // todo: parse as_compat + } + target.Val = val + return target +} + +func (c *cc) VisitJoin_source(n *parser.Join_sourceContext) interface{} { + if n == nil || len(n.AllFlatten_source()) == 0 { + return todo("VisitJoin_source", n) + } + fsList := n.AllFlatten_source() + joinOps := n.AllJoin_op() + joinConstraints := n.AllJoin_constraint() + + // todo: add ANY support + + leftNode, ok := fsList[0].Accept(c).(ast.Node) + if !ok { + return todo("VisitJoin_source", fsList[0]) + } + for i, jopCtx := range joinOps { + if i+1 >= len(fsList) { + break + } + rightNode, ok := fsList[i+1].Accept(c).(ast.Node) + if !ok { + return todo("VisitJoin_source", fsList[i+1]) + } + jexpr := &ast.JoinExpr{ + Larg: leftNode, + Rarg: rightNode, + } + if jopCtx.NATURAL() != nil { + jexpr.IsNatural = true + } + // todo: cover semi/only/exclusion/ + switch { + case jopCtx.LEFT() != nil: + jexpr.Jointype = ast.JoinTypeLeft + case jopCtx.RIGHT() != nil: + jexpr.Jointype = ast.JoinTypeRight + case jopCtx.FULL() != nil: + jexpr.Jointype = ast.JoinTypeFull + case jopCtx.INNER() != nil: + jexpr.Jointype = ast.JoinTypeInner + case jopCtx.COMMA() != nil: + jexpr.Jointype = ast.JoinTypeInner + default: + jexpr.Jointype = ast.JoinTypeInner + } + if i < len(joinConstraints) { + if jc := joinConstraints[i]; jc != nil { + switch { + case jc.ON() != nil: + if exprCtx := jc.Expr(); exprCtx != nil { + expr, ok := exprCtx.Accept(c).(ast.Node) + if !ok { + return todo("VisitJoin_source", exprCtx) + } + jexpr.Quals = expr + } + case jc.USING() != nil: + if pureListCtx := jc.Pure_column_or_named_list(); pureListCtx != nil { + var using ast.List + pureItems := pureListCtx.AllPure_column_or_named() + for _, pureCtx := range pureItems { + if anID := pureCtx.An_id(); anID != nil { + using.Items = append(using.Items, NewIdentifier(parseAnId(anID))) + } else if bp := pureCtx.Bind_parameter(); bp != nil { + bindPar, ok := bp.Accept(c).(ast.Node) + if !ok { + return todo("VisitJoin_source", bp) + } + using.Items = append(using.Items, bindPar) + } + } + jexpr.UsingClause = &using + } + default: + return todo("VisitJoin_source", jc) + } + } + } + leftNode = jexpr + } + return leftNode +} + +func (c *cc) VisitFlatten_source(n *parser.Flatten_sourceContext) interface{} { + if n == nil || n.Named_single_source() == nil { + return todo("VisitFlatten_source", n) + } + namedSingleSource, ok := n.Named_single_source().Accept(c).(ast.Node) + if !ok { + return todo("VisitFlatten_source", n.Named_single_source()) + } + return namedSingleSource +} + +func (c *cc) VisitNamed_single_source(n *parser.Named_single_sourceContext) interface{} { + if n == nil || n.Single_source() == nil { + return todo("VisitNamed_single_source", n) + } + base, ok := n.Single_source().Accept(c).(ast.Node) + if !ok { + return todo("VisitNamed_single_source", n.Single_source()) + } + + if n.AS() != nil && n.An_id() != nil { + aliasText := parseAnId(n.An_id()) + switch source := base.(type) { + case *ast.RangeVar: + source.Alias = &ast.Alias{Aliasname: &aliasText} + case *ast.RangeSubselect: + source.Alias = &ast.Alias{Aliasname: &aliasText} + } + } else if n.An_id_as_compat() != nil { //nolint + // todo: parse as_compat + } + return base +} + +func (c *cc) VisitSingle_source(n *parser.Single_sourceContext) interface{} { + if n == nil { + return todo("VisitSingle_source", n) + } + + if n.Table_ref() != nil { + tableName := n.Table_ref().GetText() // !! debug !! + return &ast.RangeVar{ + Relname: &tableName, + Location: c.pos(n.GetStart()), + } + } + + if n.Select_stmt() != nil { + subquery, ok := n.Select_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitSingle_source", n.Select_stmt()) + } + return &ast.RangeSubselect{ + Subquery: subquery, + } + + } + // todo: Values stmt + + return todo("VisitSingle_source", n) +} + +func (c *cc) VisitBind_parameter(n *parser.Bind_parameterContext) interface{} { + if n == nil || n.DOLLAR() == nil { + return todo("VisitBind_parameter", n) + } + + if n.TRUE() != nil { + return &ast.A_Const{Val: &ast.Boolean{Boolval: true}, Location: c.pos(n.GetStart())} + } + if n.FALSE() != nil { + return &ast.A_Const{Val: &ast.Boolean{Boolval: false}, Location: c.pos(n.GetStart())} + } + + if an := n.An_id_or_type(); an != nil { + idText := parseAnIdOrType(an) + return &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "@"}}}, + Rexpr: &ast.String{Str: idText}, + Location: c.pos(n.GetStart()), + } + } + return todo("VisitBind_parameter", n) +} + +func (c *cc) convertWildCardField(n *parser.Result_columnContext) *ast.ColumnRef { + prefixCtx := n.Opt_id_prefix() + prefix := c.convertOptIdPrefix(prefixCtx) + + items := []ast.Node{} + if prefix != "" { + items = append(items, NewIdentifier(prefix)) + } + + items = append(items, &ast.A_Star{}) + return &ast.ColumnRef{ + Fields: &ast.List{Items: items}, + Location: c.pos(n.GetStart()), + } +} + +func (c *cc) convertOptIdPrefix(n parser.IOpt_id_prefixContext) string { + if n == nil { + return "" + } + if n.An_id() != nil { + return n.An_id().GetText() + } + return "" +} + +func (c *cc) VisitCreate_table_stmt(n *parser.Create_table_stmtContext) interface{} { + stmt := &ast.CreateTableStmt{ + Name: parseTableName(n.Simple_table_ref().Simple_table_ref_core()), + IfNotExists: n.EXISTS() != nil, + } + for _, def := range n.AllCreate_table_entry() { + switch { + case def.Column_schema() != nil: + temp, ok := def.Column_schema().Accept(c).(ast.Node) + if !ok { + return todo("VisitCreate_table_stmt", def.Column_schema()) + } + colCtx, ok := temp.(*ast.ColumnDef) + if !ok { + return todo("VisitCreate_table_stmt", def.Column_schema()) + } + stmt.Cols = append(stmt.Cols, colCtx) + case def.Table_constraint() != nil: + conCtx := def.Table_constraint() + switch { + case conCtx.PRIMARY() != nil && conCtx.KEY() != nil: + for _, cname := range conCtx.AllAn_id() { + for _, col := range stmt.Cols { + if col.Colname == parseAnId(cname) { + col.IsNotNull = true + } + } + } + case conCtx.PARTITION() != nil && conCtx.BY() != nil: + return todo("VisitCreate_table_stmt", conCtx) + case conCtx.ORDER() != nil && conCtx.BY() != nil: + return todo("VisitCreate_table_stmt", conCtx) + } + + case def.Table_index() != nil: + return todo("VisitCreate_table_stmt", def.Table_index()) + case def.Family_entry() != nil: + return todo("VisitCreate_table_stmt", def.Family_entry()) + case def.Changefeed() != nil: // table-oriented + return todo("VisitCreate_table_stmt", def.Changefeed()) + } + } + return stmt +} + +func (c *cc) VisitColumn_schema(n *parser.Column_schemaContext) interface{} { + if n == nil { + return todo("VisitColumn_schema", n) + } + col := &ast.ColumnDef{} + + if anId := n.An_id_schema(); anId != nil { + col.Colname = identifier(parseAnIdSchema(anId)) + } + if tnb := n.Type_name_or_bind(); tnb != nil { + temp, ok := tnb.Accept(c).(ast.Node) + if !ok { + return todo("VisitColumn_schema", tnb) + } + typeName, ok := temp.(*ast.TypeName) + if !ok { + return todo("VisitColumn_schema", tnb) + } + col.TypeName = typeName + } + if colCons := n.Opt_column_constraints(); colCons != nil { + col.IsNotNull = colCons.NOT() != nil && colCons.NULL() != nil + + if colCons.DEFAULT() != nil && colCons.Expr() != nil { + defaultExpr, ok := colCons.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitColumn_schema", colCons.Expr()) + } + col.RawDefault = defaultExpr + } + } + // todo: family + + return col +} + +func (c *cc) VisitType_name_or_bind(n *parser.Type_name_or_bindContext) interface{} { + if n == nil { + return todo("VisitType_name_or_bind", n) + } + + if t := n.Type_name(); t != nil { + temp, ok := t.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_or_bind", t) + } + typeName, ok := temp.(*ast.TypeName) + if !ok { + return todo("VisitType_name_or_bind", t) + } + return typeName + } else if b := n.Bind_parameter(); b != nil { + param, ok := b.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_or_bind", b) + } + return &ast.TypeName{ + Names: &ast.List{ + Items: []ast.Node{param}, + }, + } + } + return todo("VisitType_name_or_bind", n) +} + +func (c *cc) VisitType_name(n *parser.Type_nameContext) interface{} { + if n == nil { + return todo("VisitType_name", n) + } + + questionCount := len(n.AllQUESTION()) + + if composite := n.Type_name_composite(); composite != nil { + typeName, ok := composite.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_or_bind", composite) + } + return typeName + } + + if decimal := n.Type_name_decimal(); decimal != nil { + if integerOrBinds := decimal.AllInteger_or_bind(); len(integerOrBinds) >= 2 { + first, ok := integerOrBinds[0].Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name", decimal.Integer_or_bind(0)) + } + second, ok := integerOrBinds[1].Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name", decimal.Integer_or_bind(1)) + } + name := "decimal" + if questionCount > 0 { + name = name + "?" + } + return &ast.TypeName{ + Name: name, + TypeOid: 0, + Names: &ast.List{ + Items: []ast.Node{ + first, + second, + }, + }, + } + } + } + + if simple := n.Type_name_simple(); simple != nil { + name := simple.GetText() + if questionCount > 0 { + name = name + "?" + } + return &ast.TypeName{ + Name: name, + TypeOid: 0, + } + } + + // todo: handle multiple ? suffixes + return todo("VisitType_name", n) +} + +func (c *cc) VisitInteger_or_bind(n *parser.Integer_or_bindContext) interface{} { + if n == nil { + return todo("VisitInteger_or_bind", n) + } + + if integer := n.Integer(); integer != nil { + val, err := parseIntegerValue(integer.GetText()) + if err != nil { + return todo("VisitInteger_or_bind", n.Integer()) + } + return &ast.Integer{Ival: val} + } + + if bind := n.Bind_parameter(); bind != nil { + temp, ok := bind.Accept(c).(ast.Node) + if !ok { + return todo("VisitInteger_or_bind", bind) + } + return temp + } + + return todo("VisitInteger_or_bind", n) +} + +func (c *cc) VisitType_name_composite(n *parser.Type_name_compositeContext) interface{} { + if n == nil { + return todo("VisitType_name_composite", n) + } + + if opt := n.Type_name_optional(); opt != nil { + return opt.Accept(c) + } + + if tuple := n.Type_name_tuple(); tuple != nil { + if typeNames := tuple.AllType_name_or_bind(); len(typeNames) > 0 { + var items []ast.Node + for _, tn := range typeNames { + tnNode, ok := tn.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_composite", tn) + } + items = append(items, tnNode) + } + return &ast.TypeName{ + Name: "Tuple", + TypeOid: 0, + Names: &ast.List{Items: items}, + } + } + } + + if struct_ := n.Type_name_struct(); struct_ != nil { + if structArgs := struct_.AllStruct_arg(); len(structArgs) > 0 { + var items []ast.Node + for range structArgs { + // TODO: Handle struct field names and types + items = append(items, &ast.TODO{}) + } + return &ast.TypeName{ + Name: "Struct", + TypeOid: 0, + Names: &ast.List{Items: items}, + } + } + } + + if variant := n.Type_name_variant(); variant != nil { + if variantArgs := variant.AllVariant_arg(); len(variantArgs) > 0 { + var items []ast.Node + for range variantArgs { + // TODO: Handle variant arguments + items = append(items, &ast.TODO{}) + } + return &ast.TypeName{ + Name: "Variant", + TypeOid: 0, + Names: &ast.List{Items: items}, + } + } + } + + if list := n.Type_name_list(); list != nil { + if typeName := list.Type_name_or_bind(); typeName != nil { + tn, ok := typeName.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_composite", typeName) + } + return &ast.TypeName{ + Name: "List", + TypeOid: 0, + Names: &ast.List{ + Items: []ast.Node{tn}, + }, + } + } + } + + if stream := n.Type_name_stream(); stream != nil { + if typeName := stream.Type_name_or_bind(); typeName != nil { + tn, ok := typeName.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_composite", typeName) + } + return &ast.TypeName{ + Name: "Stream", + TypeOid: 0, + Names: &ast.List{ + Items: []ast.Node{tn}, + }, + } + } + } + + if flow := n.Type_name_flow(); flow != nil { + return todo("VisitType_name_composite", flow) + } + + if dict := n.Type_name_dict(); dict != nil { + if typeNames := dict.AllType_name_or_bind(); len(typeNames) >= 2 { + first, ok := typeNames[0].Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_composite", typeNames[0]) + } + second, ok := typeNames[1].Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_composite", typeNames[1]) + } + return &ast.TypeName{ + Name: "Dict", + TypeOid: 0, + Names: &ast.List{ + Items: []ast.Node{ + first, + second, + }, + }, + } + } + } + + if set := n.Type_name_set(); set != nil { + if typeName := set.Type_name_or_bind(); typeName != nil { + tn, ok := typeName.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_composite", typeName) + } + return &ast.TypeName{ + Name: "Set", + TypeOid: 0, + Names: &ast.List{ + Items: []ast.Node{tn}, + }, + } + } + } + + if enum := n.Type_name_enum(); enum != nil { // todo: handle enum + todo("VisitType_name_composite", enum) + } + + if resource := n.Type_name_resource(); resource != nil { // todo: handle resource + todo("VisitType_name_composite", resource) + } + + if tagged := n.Type_name_tagged(); tagged != nil { // todo: handle tagged + todo("VisitType_name_composite", tagged) + } + + if callable := n.Type_name_callable(); callable != nil { // todo: handle callable + todo("VisitType_name_composite", callable) + } + + return todo("VisitType_name_composite", n) +} + +func (c *cc) VisitType_name_optional(n *parser.Type_name_optionalContext) interface{} { + if n == nil || n.Type_name_or_bind() == nil { + return todo("VisitType_name_optional", n) + } + + tn, ok := n.Type_name_or_bind().Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_optional", n.Type_name_or_bind()) + } + innerTypeName, ok := tn.(*ast.TypeName) + if !ok { + return todo("VisitType_name_optional", n.Type_name_or_bind()) + } + name := fmt.Sprintf("Optional<%s>", innerTypeName.Name) + return &ast.TypeName{ + Name: name, + TypeOid: 0, + Names: &ast.List{}, + } +} + +func (c *cc) VisitSql_stmt_core(n *parser.Sql_stmt_coreContext) interface{} { + if n == nil { + return todo("VisitSql_stmt_core", n) + } + + if stmt := n.Pragma_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Select_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Named_nodes_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_table_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Named_nodes_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_table_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_table_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Use_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Into_table_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Commit_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Update_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Delete_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Rollback_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Declare_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Import_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Export_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_table_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_external_table_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Do_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Define_action_or_subquery_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.If_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.For_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Values_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_user_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_user_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_group_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_group_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_role_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_object_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_object_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_object_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_external_data_source_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_external_data_source_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_external_data_source_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_replication_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_replication_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_topic_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_topic_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_topic_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Grant_permissions_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Revoke_permissions_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_table_store_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Upsert_object_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_view_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_view_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_replication_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_resource_pool_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_resource_pool_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_resource_pool_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_backup_collection_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_backup_collection_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_backup_collection_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Analyze_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Create_resource_pool_classifier_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_resource_pool_classifier_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Drop_resource_pool_classifier_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Backup_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Restore_stmt(); stmt != nil { + return stmt.Accept(c) + } + if stmt := n.Alter_sequence_stmt(); stmt != nil { + return stmt.Accept(c) + } + return todo("VisitSql_stmt_core", n) +} + +func (c *cc) VisitNamed_expr(n *parser.Named_exprContext) interface{} { + if n == nil || n.Expr() == nil { + return todo("VisitNamed_expr", n) + } + + expr, ok := n.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitNamed_expr", n) + } + + if n.AS() != nil && n.An_id_or_type() != nil { + name := parseAnIdOrType(n.An_id_or_type()) + return &ast.ResTarget{ + Name: &name, + Val: expr, + Location: c.pos(n.Expr().GetStart()), + } + } + return expr +} + +func (c *cc) VisitExpr(n *parser.ExprContext) interface{} { + if n == nil { + return todo("VisitExpr", n) + } + + if tn := n.Type_name_composite(); tn != nil { + return tn.Accept(c) + } + + orSubs := n.AllOr_subexpr() + if len(orSubs) == 0 { + return todo("VisitExpr", n) + } + + left, ok := n.Or_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitExpr", n) + } + + for i := 1; i < len(orSubs); i++ { + + right, ok := orSubs[i].Accept(c).(ast.Node) + if !ok { + return todo("VisitExpr", n) + } + + left = &ast.BoolExpr{ + Boolop: ast.BoolExprTypeOr, + Args: &ast.List{Items: []ast.Node{left, right}}, + Location: c.pos(n.GetStart()), + } + } + return left +} + +func (c *cc) VisitOr_subexpr(n *parser.Or_subexprContext) interface{} { + if n == nil || len(n.AllAnd_subexpr()) == 0 { + return todo("VisitOr_subexpr", n) + } + + left, ok := n.And_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitOr_subexpr", n) + } + + for i := 1; i < len(n.AllAnd_subexpr()); i++ { + + right, ok := n.And_subexpr(i).Accept(c).(ast.Node) + if !ok { + return todo("VisitOr_subexpr", n) + } + + left = &ast.BoolExpr{ + Boolop: ast.BoolExprTypeAnd, + Args: &ast.List{Items: []ast.Node{left, right}}, + Location: c.pos(n.GetStart()), + } + } + return left +} + +func (c *cc) VisitAnd_subexpr(n *parser.And_subexprContext) interface{} { + if n == nil || len(n.AllXor_subexpr()) == 0 { + return todo("VisitAnd_subexpr", n) + } + + left, ok := n.Xor_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitAnd_subexpr", n) + } + + for i := 1; i < len(n.AllXor_subexpr()); i++ { + + right, ok := n.Xor_subexpr(i).Accept(c).(ast.Node) + if !ok { + return todo("VisitAnd_subexpr", n) + } + + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "XOR"}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(n.GetStart()), + } + } + return left +} + +func (c *cc) VisitXor_subexpr(n *parser.Xor_subexprContext) interface{} { + if n == nil || n.Eq_subexpr() == nil { + return todo("VisitXor_subexpr", n) + } + + base, ok := n.Eq_subexpr().Accept(c).(ast.Node) + if !ok { + return todo("VisitXor_subexpr", n) + } + + if condCtx := n.Cond_expr(); condCtx != nil { + + switch { + case condCtx.IN() != nil: + if inExpr := condCtx.In_expr(); inExpr != nil { + temp, ok := inExpr.Accept(c).(ast.Node) + if !ok { + return todo("VisitXor_subexpr", inExpr) + } + list, ok := temp.(*ast.List) + if !ok { + return todo("VisitXor_subexpr", inExpr) + } + return &ast.In{ + Expr: base, + List: list.Items, + Not: condCtx.NOT() != nil, + Location: c.pos(n.GetStart()), + } + } + case condCtx.BETWEEN() != nil: + if eqSubs := condCtx.AllEq_subexpr(); len(eqSubs) >= 2 { + + first, ok := eqSubs[0].Accept(c).(ast.Node) + if !ok { + return todo("VisitXor_subexpr", n) + } + + second, ok := eqSubs[1].Accept(c).(ast.Node) + if !ok { + return todo("VisitXor_subexpr", n) + } + + return &ast.BetweenExpr{ + Expr: base, + Left: first, + Right: second, + Not: condCtx.NOT() != nil, + Location: c.pos(n.GetStart()), + } + } + case condCtx.ISNULL() != nil: + return &ast.NullTest{ + Arg: base, + Nulltesttype: 1, // IS NULL + Location: c.pos(n.GetStart()), + } + case condCtx.NOTNULL() != nil: + return &ast.NullTest{ + Arg: base, + Nulltesttype: 2, // IS NOT NULL + Location: c.pos(n.GetStart()), + } + case condCtx.IS() != nil && condCtx.NULL() != nil: + return &ast.NullTest{ + Arg: base, + Nulltesttype: 1, // IS NULL + Location: c.pos(n.GetStart()), + } + case condCtx.NOT() != nil && condCtx.NULL() != nil: + return &ast.NullTest{ + Arg: base, + Nulltesttype: 2, // IS NOT NULL + Location: c.pos(n.GetStart()), + } + case condCtx.Match_op() != nil: + // debug!!! + matchOp := condCtx.Match_op().GetText() + if eqSubs := condCtx.AllEq_subexpr(); len(eqSubs) >= 1 { + + xpr, ok := eqSubs[0].Accept(c).(ast.Node) + if !ok { + return todo("VisitXor_subexpr", n) + } + + expr := &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: matchOp}}}, + Lexpr: base, + Rexpr: xpr, + } + if condCtx.ESCAPE() != nil && len(eqSubs) >= 2 { //nolint + // todo: Add ESCAPE support + } + return expr + } + case len(condCtx.AllEQUALS()) > 0 || len(condCtx.AllEQUALS2()) > 0 || + len(condCtx.AllNOT_EQUALS()) > 0 || len(condCtx.AllNOT_EQUALS2()) > 0: + eqSubs := condCtx.AllEq_subexpr() + if len(eqSubs) >= 1 { + left := base + + ops := c.collectEqualityOps(condCtx) + + for i, eqSub := range eqSubs { + right, ok := eqSub.Accept(c).(ast.Node) + if !ok { + return todo("VisitXor_subexpr", condCtx) + } + + var op string + if i < len(ops) { + op = ops[i].GetText() + } else { + if len(condCtx.AllEQUALS()) > 0 { + op = "=" + } else if len(condCtx.AllEQUALS2()) > 0 { + op = "==" + } else if len(condCtx.AllNOT_EQUALS()) > 0 { + op = "!=" + } else if len(condCtx.AllNOT_EQUALS2()) > 0 { + op = "<>" + } + } + + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: op}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(condCtx.GetStart()), + } + } + return left + } + return todo("VisitXor_subexpr", condCtx) + case len(condCtx.AllDistinct_from_op()) > 0: + // debug!!! + distinctOps := condCtx.AllDistinct_from_op() + for _, distinctOp := range distinctOps { + if eqSubs := condCtx.AllEq_subexpr(); len(eqSubs) >= 1 { + not := distinctOp.NOT() != nil + op := "IS DISTINCT FROM" + if not { + op = "IS NOT DISTINCT FROM" + } + + xpr, ok := eqSubs[0].Accept(c).(ast.Node) + if !ok { + return todo("VisitXor_subexpr", n) + } + + return &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: op}}}, + Lexpr: base, + Rexpr: xpr, + } + } + } + } + } + return base +} + +func (c *cc) VisitEq_subexpr(n *parser.Eq_subexprContext) interface{} { + if n == nil || len(n.AllNeq_subexpr()) == 0 { + return todo("VisitEq_subexpr", n) + } + + left, ok := n.Neq_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitEq_subexpr", n) + } + + ops := c.collectComparisonOps(n) + for i := 1; i < len(n.AllNeq_subexpr()); i++ { + + right, ok := n.Neq_subexpr(i).Accept(c).(ast.Node) + if !ok { + return todo("VisitEq_subexpr", n) + } + + opText := ops[i-1].GetText() + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: opText}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(n.GetStart()), + } + } + return left +} + +func (c *cc) VisitNeq_subexpr(n *parser.Neq_subexprContext) interface{} { + if n == nil || len(n.AllBit_subexpr()) == 0 { + return todo("VisitNeq_subexpr", n) + } + + left, ok := n.Bit_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitNeq_subexpr", n) + } + + ops := c.collectBitwiseOps(n) + for i := 1; i < len(n.AllBit_subexpr()); i++ { + right, ok := n.Bit_subexpr(i).Accept(c).(ast.Node) + if !ok { + return todo("VisitNeq_subexpr", n) + } + opText := ops[i-1].GetText() + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: opText}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(n.GetStart()), + } + } + + if n.Double_question() != nil { + if nextCtx := n.Neq_subexpr(); nextCtx != nil { + right, ok2 := nextCtx.Accept(c).(ast.Node) + if !ok2 { + return todo("VisitNeq_subexpr", n) + } + + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "??"}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(n.GetStart()), + } + } + } else { + // !! debug !! + qCount := len(n.AllQUESTION()) + if qCount > 0 { + questionOp := "?" + if qCount > 1 { + questionOp = strings.Repeat("?", qCount) + } + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: questionOp}}}, + Lexpr: left, + Location: c.pos(n.GetStart()), + } + } + } + + return left +} + +func (c *cc) VisitBit_subexpr(n *parser.Bit_subexprContext) interface{} { + if n == nil || len(n.AllAdd_subexpr()) == 0 { + return todo("VisitBit_subexpr", n) + } + + left, ok := n.Add_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitBit_subexpr", n) + } + + ops := c.collectBitOps(n) + for i := 1; i < len(n.AllAdd_subexpr()); i++ { + + right, ok := n.Add_subexpr(i).Accept(c).(ast.Node) + if !ok { + return todo("VisitBit_subexpr", n) + } + + opText := ops[i-1].GetText() + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: opText}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(n.GetStart()), + } + } + return left +} + +func (c *cc) VisitAdd_subexpr(n *parser.Add_subexprContext) interface{} { + if n == nil || len(n.AllMul_subexpr()) == 0 { + return todo("VisitAdd_subexpr", n) + } + + left, ok := n.Mul_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitAdd_subexpr", n) + } + + ops := c.collectAddOps(n) + for i := 1; i < len(n.AllMul_subexpr()); i++ { + + right, ok := n.Mul_subexpr(i).Accept(c).(ast.Node) + if !ok { + return todo("VisitAdd_subexpr", n) + } + + opText := ops[i-1].GetText() + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: opText}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(n.GetStart()), + } + } + return left +} + +func (c *cc) VisitMul_subexpr(n *parser.Mul_subexprContext) interface{} { + if n == nil || len(n.AllCon_subexpr()) == 0 { + return todo("VisitMul_subexpr", n) + } + + left, ok := n.Con_subexpr(0).Accept(c).(ast.Node) + if !ok { + return todo("VisitMul_subexpr", n) + } + + for i := 1; i < len(n.AllCon_subexpr()); i++ { + + right, ok := n.Con_subexpr(i).Accept(c).(ast.Node) + if !ok { + return todo("VisitMul_subexpr", n) + } + + left = &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: "||"}}}, + Lexpr: left, + Rexpr: right, + Location: c.pos(n.GetStart()), + } + } + return left +} + +func (c *cc) VisitCon_subexpr(n *parser.Con_subexprContext) interface{} { + if n == nil || (n.Unary_op() == nil && n.Unary_subexpr() == nil) { + return todo("VisitCon_subexpr", n) + } + + if opCtx := n.Unary_op(); opCtx != nil { + op := opCtx.GetText() + operand, ok := n.Unary_subexpr().Accept(c).(ast.Node) + if !ok { + return todo("VisitCon_subexpr", opCtx) + } + return &ast.A_Expr{ + Name: &ast.List{Items: []ast.Node{&ast.String{Str: op}}}, + Rexpr: operand, + Location: c.pos(n.GetStart()), + } + } + + operand, ok := n.Unary_subexpr().Accept(c).(ast.Node) + if !ok { + return todo("VisitCon_subexpr", n.Unary_subexpr()) + } + return operand + +} + +func (c *cc) VisitUnary_subexpr(n *parser.Unary_subexprContext) interface{} { + if n == nil || (n.Unary_casual_subexpr() == nil && n.Json_api_expr() == nil) { + return todo("VisitUnary_subexpr", n) + } + + if casual := n.Unary_casual_subexpr(); casual != nil { + expr, ok := casual.Accept(c).(ast.Node) + if !ok { + return todo("VisitUnary_subexpr", casual) + } + return expr + } + if jsonExpr := n.Json_api_expr(); jsonExpr != nil { + expr, ok := jsonExpr.Accept(c).(ast.Node) + if !ok { + return todo("VisitUnary_subexpr", jsonExpr) + } + return expr + } + + return todo("VisitUnary_subexpr", n) +} + +func (c *cc) VisitJson_api_expr(n *parser.Json_api_exprContext) interface{} { + return todo("VisitJson_api_expr", n) +} + +func (c *cc) VisitUnary_casual_subexpr(n *parser.Unary_casual_subexprContext) interface{} { + var current ast.Node + switch { + case n.Id_expr() != nil: + expr, ok := n.Id_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitUnary_casual_subexpr", n.Id_expr()) + } + current = expr + case n.Atom_expr() != nil: + expr, ok := n.Atom_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitUnary_casual_subexpr", n.Atom_expr()) + } + current = expr + default: + return todo("VisitUnary_casual_subexpr", n) + } + + if suffix := n.Unary_subexpr_suffix(); suffix != nil { + current = c.processSuffixChain(current, suffix.(*parser.Unary_subexpr_suffixContext)) + } + + return current +} + +func (c *cc) processSuffixChain(base ast.Node, suffix *parser.Unary_subexpr_suffixContext) ast.Node { + current := base + for i := 0; i < suffix.GetChildCount(); i++ { + child := suffix.GetChild(i) + switch elem := child.(type) { + case *parser.Key_exprContext: + current = c.handleKeySuffix(current, elem) + case *parser.Invoke_exprContext: + current = c.handleInvokeSuffix(current, elem, i) + case antlr.TerminalNode: + if elem.GetText() == "." { + current = c.handleDotSuffix(current, suffix, &i) + } else { + return todo("Unary_subexpr_suffixContext", suffix) + } + default: + return todo("Unary_subexpr_suffixContext", suffix) + } + } + return current +} + +func (c *cc) handleKeySuffix(base ast.Node, keyCtx *parser.Key_exprContext) ast.Node { + keyNode, ok := keyCtx.Accept(c).(ast.Node) + if !ok { + return todo("VisitKey_expr", keyCtx) + } + ind, ok := keyNode.(*ast.A_Indirection) + if !ok { + return todo("VisitKey_expr", keyCtx) + } + + if indirection, ok := base.(*ast.A_Indirection); ok { + indirection.Indirection.Items = append(indirection.Indirection.Items, ind.Indirection.Items...) + return indirection + } + + return &ast.A_Indirection{ + Arg: base, + Indirection: &ast.List{ + Items: []ast.Node{keyNode}, + }, + } +} + +func (c *cc) handleInvokeSuffix(base ast.Node, invokeCtx *parser.Invoke_exprContext, idx int) ast.Node { + temp, ok := invokeCtx.Accept(c).(ast.Node) + if !ok { + return todo("VisitInvoke_expr", invokeCtx) + } + funcCall, ok := temp.(*ast.FuncCall) + if !ok { + return todo("VisitInvoke_expr", invokeCtx) + } + + if idx == 0 { + switch baseNode := base.(type) { + case *ast.ColumnRef: + if len(baseNode.Fields.Items) > 0 { + var nameParts []string + for _, item := range baseNode.Fields.Items { + if s, ok := item.(*ast.String); ok { + nameParts = append(nameParts, s.Str) + } + } + funcName := strings.Join(nameParts, ".") + + if funcName == "coalesce" || funcName == "nvl" { + return &ast.CoalesceExpr{ + Args: funcCall.Args, + Location: baseNode.Location, + } + } + + if funcName == "greatest" || funcName == "max_of" { + return &ast.MinMaxExpr{ + Op: ast.MinMaxOp(1), + Args: funcCall.Args, + Location: baseNode.Location, + } + } + if funcName == "least" || funcName == "min_of" { + return &ast.MinMaxExpr{ + Op: ast.MinMaxOp(2), + Args: funcCall.Args, + Location: baseNode.Location, + } + } + + funcCall.Func = &ast.FuncName{Name: funcName} + funcCall.Funcname.Items = append(funcCall.Funcname.Items, &ast.String{Str: funcName}) + + return funcCall + } + default: + return todo("VisitInvoke_expr", invokeCtx) + } + } + + stmt := &ast.FuncExpr{ + Xpr: base, + Args: funcCall.Args, + Location: funcCall.Location, + } + + return stmt +} + +func (c *cc) handleDotSuffix(base ast.Node, suffix *parser.Unary_subexpr_suffixContext, idx *int) ast.Node { + if *idx+1 >= suffix.GetChildCount() { + return base + } + + next := suffix.GetChild(*idx + 1) + *idx++ + + var field ast.Node + switch v := next.(type) { + case *parser.Bind_parameterContext: + temp, ok := v.Accept(c).(ast.Node) + if !ok { + return todo("VisitBind_parameter", v) + } + field = temp + case *parser.An_id_or_typeContext: + field = &ast.String{Str: parseAnIdOrType(v)} + case antlr.TerminalNode: + if val, err := parseIntegerValue(v.GetText()); err == nil { + field = &ast.A_Const{Val: &ast.Integer{Ival: val}} + } else { + return todo("Unary_subexpr_suffixContext", suffix) + } + } + + if field == nil { + return base + } + + if cr, ok := base.(*ast.ColumnRef); ok { + cr.Fields.Items = append(cr.Fields.Items, field) + return cr + } + return &ast.ColumnRef{ + Fields: &ast.List{Items: []ast.Node{base, field}}, + } +} + +func (c *cc) VisitKey_expr(n *parser.Key_exprContext) interface{} { + if n.LBRACE_SQUARE() == nil || n.RBRACE_SQUARE() == nil || n.Expr() == nil { + return todo("VisitKey_expr", n) + } + + stmt := &ast.A_Indirection{ + Indirection: &ast.List{}, + } + + expr, ok := n.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitKey_expr", n.Expr()) + } + + stmt.Indirection.Items = append(stmt.Indirection.Items, &ast.A_Indices{ + Uidx: expr, + }) + + return stmt +} + +func (c *cc) VisitInvoke_expr(n *parser.Invoke_exprContext) interface{} { + if n.LPAREN() == nil || n.RPAREN() == nil { + return todo("VisitInvoke_expr", n) + } + + distinct := false + if n.Opt_set_quantifier() != nil { + distinct = n.Opt_set_quantifier().DISTINCT() != nil + } + + stmt := &ast.FuncCall{ + AggDistinct: distinct, + Funcname: &ast.List{}, + AggOrder: &ast.List{}, + Args: &ast.List{}, + Location: c.pos(n.GetStart()), + } + + if nList := n.Named_expr_list(); nList != nil { + for _, namedExpr := range nList.AllNamed_expr() { + name := parseAnIdOrType(namedExpr.An_id_or_type()) + expr, ok := namedExpr.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitInvoke_expr", namedExpr.Expr()) + } + + var res ast.Node + if rt, ok := expr.(*ast.ResTarget); ok { + if name != "" { + rt.Name = &name + } + res = rt + } else if name != "" { + res = &ast.ResTarget{ + Name: &name, + Val: expr, + Location: c.pos(namedExpr.Expr().GetStart()), + } + } else { + res = expr + } + + stmt.Args.Items = append(stmt.Args.Items, res) + } + } else if n.ASTERISK() != nil { + stmt.AggStar = true + } + + return stmt +} + +func (c *cc) VisitId_expr(n *parser.Id_exprContext) interface{} { + if n == nil { + return todo("VisitId_expr", n) + } + + ref := &ast.ColumnRef{ + Fields: &ast.List{}, + Location: c.pos(n.GetStart()), + } + + if id := n.Identifier(); id != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(id.GetText())) + return ref + } + + if keyword := n.Keyword_compat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + if keyword := n.Keyword_alter_uncompat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + if keyword := n.Keyword_in_uncompat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + if keyword := n.Keyword_window_uncompat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + if keyword := n.Keyword_hint_uncompat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + return todo("VisitId_expr", n) +} + +func (c *cc) VisitAtom_expr(n *parser.Atom_exprContext) interface{} { + if n == nil { + return todo("VisitAtom_expr", n) + } + + switch { + case n.An_id_or_type() != nil: + if n.NAMESPACE() != nil { + return NewIdentifier(parseAnIdOrType(n.An_id_or_type()) + "::" + parseIdOrType(n.Id_or_type())) + } + return NewIdentifier(parseAnIdOrType(n.An_id_or_type())) + case n.Literal_value() != nil: + expr, ok := n.Literal_value().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Literal_value()) + } + return expr + case n.Bind_parameter() != nil: + expr, ok := n.Bind_parameter().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Bind_parameter()) + } + return expr + case n.Cast_expr() != nil: + expr, ok := n.Cast_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Cast_expr()) + } + return expr + // TODO: check other cases + default: + return todo("VisitAtom_expr", n) + } +} + +func (c *cc) VisitCast_expr(n *parser.Cast_exprContext) interface{} { + if n == nil || n.CAST() == nil || n.Expr() == nil || n.AS() == nil || n.Type_name_or_bind() == nil { + return todo("VisitCast_expr", n) + } + + expr, ok := n.Expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitCast_expr", n.Expr()) + } + + temp, ok := n.Type_name_or_bind().Accept(c).(ast.Node) + if !ok { + return todo("VisitCast_expr", n.Type_name_or_bind()) + } + typeName, ok := temp.(*ast.TypeName) + if !ok { + return todo("VisitCast_expr", n.Type_name_or_bind()) + } + + return &ast.TypeCast{ + Arg: expr, + TypeName: typeName, + Location: c.pos(n.GetStart()), + } +} + +func (c *cc) VisitLiteral_value(n *parser.Literal_valueContext) interface{} { + if n == nil { + return todo("VisitLiteral_value", n) + } + + switch { + case n.Integer() != nil: + text := n.Integer().GetText() + val, err := parseIntegerValue(text) + if err != nil { + if debug.Active { + log.Printf("Failed to parse integer value '%s': %v", text, err) + } + return todo("VisitLiteral_value", n.Integer()) + } + return &ast.A_Const{Val: &ast.Integer{Ival: val}, Location: c.pos(n.GetStart())} + + case n.Real_() != nil: + text := n.Real_().GetText() + return &ast.A_Const{Val: &ast.Float{Str: text}, Location: c.pos(n.GetStart())} + + case n.STRING_VALUE() != nil: // !!! debug !!! (problem with quoted strings) + val := n.STRING_VALUE().GetText() + if len(val) >= 2 { + val = val[1 : len(val)-1] + } + return &ast.A_Const{Val: &ast.String{Str: val}, Location: c.pos(n.GetStart())} + + case n.Bool_value() != nil: + var i bool + if n.Bool_value().TRUE() != nil { + i = true + } + return &ast.A_Const{Val: &ast.Boolean{Boolval: i}, Location: c.pos(n.GetStart())} + + case n.NULL() != nil: + return &ast.Null{} + + case n.CURRENT_TIME() != nil: + log.Fatalf("CURRENT_TIME is not supported yet") + return todo("VisitLiteral_value", n) + + case n.CURRENT_DATE() != nil: + log.Fatalf("CURRENT_DATE is not supported yet") + return todo("VisitLiteral_value", n) + + case n.CURRENT_TIMESTAMP() != nil: + log.Fatalf("CURRENT_TIMESTAMP is not supported yet") + return todo("VisitLiteral_value", n) + + case n.BLOB() != nil: + blobText := n.BLOB().GetText() + return &ast.A_Const{Val: &ast.String{Str: blobText}, Location: c.pos(n.GetStart())} + + case n.EMPTY_ACTION() != nil: + if debug.Active { + log.Printf("TODO: Implement EMPTY_ACTION") + } + return &ast.TODO{} + + default: + return todo("VisitLiteral_value", n) + } +} + +func (c *cc) VisitSql_stmt(n *parser.Sql_stmtContext) interface{} { + if n == nil || n.Sql_stmt_core() == nil { + return todo("VisitSql_stmt", n) + } + + expr, ok := n.Sql_stmt_core().Accept(c).(ast.Node) + if !ok { + return todo("VisitSql_stmt", n.Sql_stmt_core()) + } + + if n.EXPLAIN() != nil { + options := &ast.List{Items: []ast.Node{}} + + if n.QUERY() != nil && n.PLAN() != nil { + queryPlan := "QUERY PLAN" + options.Items = append(options.Items, &ast.DefElem{ + Defname: &queryPlan, + Arg: &ast.TODO{}, + }) + } + + return &ast.ExplainStmt{ + Query: expr, + Options: options, + } + } + + return expr +} diff --git a/internal/engine/ydb/lib/aggregate.go b/internal/engine/ydb/lib/aggregate.go new file mode 100644 index 0000000000..7c5d795eca --- /dev/null +++ b/internal/engine/ydb/lib/aggregate.go @@ -0,0 +1,407 @@ +package lib + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func AggregateFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, countFuncs()...) + funcs = append(funcs, minMaxFuncs()...) + funcs = append(funcs, sumFuncs()...) + funcs = append(funcs, avgFuncs()...) + funcs = append(funcs, countIfFuncs()...) + funcs = append(funcs, sumIfFuncs()...) + funcs = append(funcs, avgIfFuncs()...) + funcs = append(funcs, someFuncs()...) + funcs = append(funcs, countDistinctEstimateHLLFuncs()...) + funcs = append(funcs, maxByMinByFuncs()...) + funcs = append(funcs, stddevVarianceFuncs()...) + funcs = append(funcs, correlationCovarianceFuncs()...) + funcs = append(funcs, percentileMedianFuncs()...) + funcs = append(funcs, boolAndOrXorFuncs()...) + funcs = append(funcs, bitAndOrXorFuncs()...) + + // TODO: Aggregate_List, Top, Bottom, Top_By, Bottom_By, TopFreq, Mode, + // Histogram LinearHistogram, LogarithmicHistogram, LogHistogram, CDF, + // SessionStart, AGGREGATE_BY, MULTI_AGGREGATE_BY + + return funcs +} + +func countFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "COUNT", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func minMaxFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "MIN", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "MAX", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func sumFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "SUM", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func avgFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "AVG", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func countIfFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "COUNT_IF", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + } +} + +func sumIfFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "SUM_IF", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func avgIfFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "AVG_IF", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func someFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "SOME", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func countDistinctEstimateHLLFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "CountDistinctEstimate", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "HyperLogLog", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "HLL", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + } +} + +func maxByMinByFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "MAX_BY", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "MIN_BY", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } + // todo: min/max_by with third argument returning list +} + +func stddevVarianceFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "STDDEV", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "STDDEV_POPULATION", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "POPULATION_STDDEV", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "STDDEV_SAMPLE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "STDDEVSAMP", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "VARIANCE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "VARIANCE_POPULATION", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "POPULATION_VARIANCE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "VARPOP", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "VARIANCE_SAMPLE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + } +} + +func correlationCovarianceFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "CORRELATION", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "COVARIANCE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "COVARIANCE_SAMPLE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "COVARIANCE_POPULATION", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + } +} + +func percentileMedianFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "PERCENTILE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "MEDIAN", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "MEDIAN", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func boolAndOrXorFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "BOOL_AND", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + ReturnTypeNullable: true, + }, + { + Name: "BOOL_OR", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + ReturnTypeNullable: true, + }, + { + Name: "BOOL_XOR", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + ReturnTypeNullable: true, + }, + } +} + +func bitAndOrXorFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "BIT_AND", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "BIT_OR", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "BIT_XOR", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} diff --git a/internal/engine/ydb/lib/basic.go b/internal/engine/ydb/lib/basic.go new file mode 100644 index 0000000000..d5cbfda950 --- /dev/null +++ b/internal/engine/ydb/lib/basic.go @@ -0,0 +1,713 @@ +package lib + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func BasicFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, lengthFuncs()...) + funcs = append(funcs, substringFuncs()...) + funcs = append(funcs, findFuncs()...) + funcs = append(funcs, rfindFuncs()...) + funcs = append(funcs, startsWithFuncs()...) + funcs = append(funcs, endsWithFuncs()...) + funcs = append(funcs, ifFuncs()...) + funcs = append(funcs, nanvlFuncs()...) + funcs = append(funcs, randomFuncs()...) + funcs = append(funcs, currentUtcFuncs()...) + funcs = append(funcs, currentTzFuncs()...) + funcs = append(funcs, addTimezoneFuncs()...) + funcs = append(funcs, removeTimezoneFuncs()...) + funcs = append(funcs, versionFuncs()...) + funcs = append(funcs, ensureFuncs()...) + funcs = append(funcs, assumeStrictFuncs()...) + funcs = append(funcs, likelyFuncs()...) + funcs = append(funcs, evaluateFuncs()...) + funcs = append(funcs, simpleTypesLiteralsFuncs()...) + funcs = append(funcs, toFromBytesFuncs()...) + funcs = append(funcs, byteAtFuncs()...) + funcs = append(funcs, testClearSetFlipBitFuncs()...) + funcs = append(funcs, absFuncs()...) + funcs = append(funcs, justUnwrapNothingFuncs()...) + funcs = append(funcs, pickleUnpickleFuncs()...) + + // todo: implement functions: + // Udf, AsTuple, AsStruct, AsList, AsDict, AsSet, AsListStrict, AsDictStrict, AsSetStrict, + // Variant, AsVariant, Visit, VisitOrDefault, VariantItem, Way, DynamicVariant, + // Enum, AsEnum, AsTagged, Untag, TableRow, Callable, + // StaticMap, StaticZip, StaticFold, StaticFold1, + // AggregationFactory, AggregateTransformInput, AggregateTransformOutput, AggregateFlatten + + return funcs +} + +func lengthFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "LENGTH", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "LEN", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + } +} + +func substringFuncs() []*catalog.Function { + funcs := []*catalog.Function{ + { + Name: "Substring", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Substring", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } + return funcs +} + +func findFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Find", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Find", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + } +} + +func rfindFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "RFind", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "RFind", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + } +} + +func startsWithFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "StartsWith", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func endsWithFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "EndsWith", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func ifFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "IF", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "IF", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: false, + }, + } +} + +func nanvlFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "NANVL", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func randomFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Random", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "RandomNumber", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "RandomUuid", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "Uuid"}, + }, + } +} + +func currentUtcFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "CurrentUtcDate", + Args: []*catalog.Argument{ + { + Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "Date"}, + }, + { + Name: "CurrentUtcDatetime", + Args: []*catalog.Argument{ + { + Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "Datetime"}, + }, + { + Name: "CurrentUtcTimestamp", + Args: []*catalog.Argument{ + { + Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "Timestamp"}, + }, + } +} + +func currentTzFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "CurrentTzDate", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + { + Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "TzDate"}, + }, + { + Name: "CurrentTzDatetime", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + { + Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "TzDatetime"}, + }, + { + Name: "CurrentTzTimestamp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + { + Type: &ast.TypeName{Name: "any"}, + Mode: ast.FuncParamVariadic, + }, + }, + ReturnType: &ast.TypeName{Name: "TzTimestamp"}, + }, + } +} + +func addTimezoneFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "AddTimezone", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func removeTimezoneFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "RemoveTimezone", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func versionFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Version", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func ensureFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Ensure", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "EnsureType", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "EnsureConvertibleTo", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func assumeStrictFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "AssumeStrict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func likelyFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Likely", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func evaluateFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "EvaluateExpr", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "EvaluateAtom", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func simpleTypesLiteralsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Bool", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Uint8", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "Int32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Int32"}, + }, + { + Name: "Uint32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Int64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Int64"}, + }, + { + Name: "Uint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Float", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Float"}, + }, + { + Name: "Double", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Decimal", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint8"}}, // precision + {Type: &ast.TypeName{Name: "Uint8"}}, // scale + }, + ReturnType: &ast.TypeName{Name: "Decimal"}, + }, + { + Name: "String", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Utf8", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Yson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Yson"}, + }, + { + Name: "Json", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Json"}, + }, + { + Name: "Date", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Date"}, + }, + { + Name: "Datetime", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Datetime"}, + }, + { + Name: "Timestamp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp"}, + }, + { + Name: "Interval", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval"}, + }, + { + Name: "TzDate", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "TzDate"}, + }, + { + Name: "TzDatetime", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "TzDatetime"}, + }, + { + Name: "TzTimestamp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "TzTimestamp"}, + }, + { + Name: "Uuid", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uuid"}, + }, + } +} + +func toFromBytesFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "ToBytes", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "FromBytes", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func byteAtFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "ByteAt", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + } +} + +func testClearSetFlipBitFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "TestBit", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "ClearBit", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint8"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "SetBit", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint8"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "FlipBit", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint8"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func absFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Abs", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func justUnwrapNothingFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Just", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "Unwrap", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Unwrap", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Nothing", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func pickleUnpickleFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pickle", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "StablePickle", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Unpickle", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp.go b/internal/engine/ydb/lib/cpp.go new file mode 100644 index 0000000000..9f076aba98 --- /dev/null +++ b/internal/engine/ydb/lib/cpp.go @@ -0,0 +1,27 @@ +package lib + +import ( + "github.com/sqlc-dev/sqlc/internal/engine/ydb/lib/cpp" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func CppFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, cpp.DateTimeFunctions()...) + funcs = append(funcs, cpp.DigestFunctions()...) + funcs = append(funcs, cpp.HyperscanFunctions()...) + funcs = append(funcs, cpp.IpFunctions()...) + funcs = append(funcs, cpp.MathFunctions()...) + funcs = append(funcs, cpp.PcreFunctions()...) + funcs = append(funcs, cpp.PireFunctions()...) + funcs = append(funcs, cpp.Re2Functions()...) + funcs = append(funcs, cpp.StringFunctions()...) + funcs = append(funcs, cpp.UnicodeFunctions()...) + funcs = append(funcs, cpp.UrlFunctions()...) + funcs = append(funcs, cpp.YsonFunctions()...) + + // TODO: Histogram library, KNN library, PostgeSQL library + + return funcs +} diff --git a/internal/engine/ydb/lib/cpp/datetime.go b/internal/engine/ydb/lib/cpp/datetime.go new file mode 100644 index 0000000000..ca6f6bc6b6 --- /dev/null +++ b/internal/engine/ydb/lib/cpp/datetime.go @@ -0,0 +1,695 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func DateTimeFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, dateTimeMakeFuncs()...) + funcs = append(funcs, dateTimeGetFuncs()...) + funcs = append(funcs, dateTimeUpdateFuncs()...) + funcs = append(funcs, dateTimeFromFuncs()...) + funcs = append(funcs, dateTimeToFuncs()...) + funcs = append(funcs, dateTimeIntervalFuncs()...) + funcs = append(funcs, dateTimeStartEndFuncs()...) + funcs = append(funcs, dateTimeFormatFuncs()...) + funcs = append(funcs, dateTimeParseFuncs()...) + + return funcs +} + +func dateTimeMakeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::MakeDate", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Date"}, + }, + { + Name: "DateTime::MakeDate32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Date32"}, + }, + { + Name: "DateTime::MakeTzDate32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "TzDate32"}, + }, + { + Name: "DateTime::MakeDatetime", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Datetime"}, + }, + { + Name: "DateTime::MakeTzDatetime", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "TzDatetime"}, + }, + { + Name: "DateTime::MakeDatetime64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Datetime64"}, + }, + { + Name: "DateTime::MakeTzDatetime64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "TzDatetime64"}, + }, + { + Name: "DateTime::MakeTimestamp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp"}, + }, + { + Name: "DateTime::MakeTzTimestamp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "TzTimestamp"}, + }, + { + Name: "DateTime::MakeTimestamp64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp64"}, + }, + { + Name: "DateTime::MakeTzTimestamp64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "TzTimestamp64"}, + }, + } +} + +func dateTimeGetFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::GetYear", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint16"}, + }, + { + Name: "DateTime::GetYear", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Int32"}, + }, + { + Name: "DateTime::GetDayOfYear", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint16"}, + }, + { + Name: "DateTime::GetMonth", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetMonthName", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "DateTime::GetWeekOfYear", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetWeekOfYearIso8601", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetDayOfMonth", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetDayOfWeek", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetDayOfWeekName", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "DateTime::GetHour", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetMinute", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetSecond", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint8"}, + }, + { + Name: "DateTime::GetMillisecondOfSecond", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "DateTime::GetMicrosecondOfSecond", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "DateTime::GetTimezoneId", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint16"}, + }, + { + Name: "DateTime::GetTimezoneName", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func dateTimeUpdateFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::Update", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func dateTimeFromFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::FromSeconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp"}, + }, + { + Name: "DateTime::FromSeconds64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp64"}, + }, + { + Name: "DateTime::FromMilliseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp"}, + }, + { + Name: "DateTime::FromMilliseconds64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp64"}, + }, + { + Name: "DateTime::FromMicroseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp"}, + }, + { + Name: "DateTime::FromMicroseconds64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Timestamp64"}, + }, + } +} + +func dateTimeToFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::ToSeconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ToMilliseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ToMicroseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func dateTimeIntervalFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::ToDays", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ToHours", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ToMinutes", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ToSeconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ToMilliseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ToMicroseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::IntervalFromDays", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int32"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval"}, + }, + { + Name: "DateTime::Interval64FromDays", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int32"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval64"}, + }, + { + Name: "DateTime::IntervalFromHours", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int32"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval"}, + }, + { + Name: "DateTime::Interval64FromHours", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval64"}, + }, + { + Name: "DateTime::IntervalFromMinutes", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int32"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval"}, + }, + { + Name: "DateTime::Interval64FromMinutes", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval64"}, + }, + { + Name: "DateTime::IntervalFromSeconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval"}, + }, + { + Name: "DateTime::Interval64FromSeconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval64"}, + }, + { + Name: "DateTime::IntervalFromMilliseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval"}, + }, + { + Name: "DateTime::Interval64FromMilliseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval64"}, + }, + { + Name: "DateTime::IntervalFromMicroseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval"}, + }, + { + Name: "DateTime::Interval64FromMicroseconds", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Interval64"}, + }, + } +} + +func dateTimeStartEndFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::StartOfYear", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::EndOfYear", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::StartOfQuarter", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::EndOfQuarter", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::StartOfMonth", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::EndOfMonth", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::StartOfWeek", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::EndOfWeek", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::StartOfDay", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::EndOfDay", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::StartOf", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::EndOf", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func dateTimeFormatFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::Format", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func dateTimeParseFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "DateTime::Parse", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::Parse64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "DateTime::ParseRfc822", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::ParseIso8601", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::ParseHttp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "DateTime::ParseX509", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/digest.go b/internal/engine/ydb/lib/cpp/digest.go new file mode 100644 index 0000000000..dccdb8509b --- /dev/null +++ b/internal/engine/ydb/lib/cpp/digest.go @@ -0,0 +1,171 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func DigestFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, digestCrcFuncs()...) + funcs = append(funcs, digestFnvFuncs()...) + funcs = append(funcs, digestMurmurFuncs()...) + funcs = append(funcs, digestCityFuncs()...) + + return funcs +} + +func digestCrcFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Digest::Crc32c", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Digest::Crc64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::Crc64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func digestFnvFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Digest::Fnv32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Digest::Fnv32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Digest::Fnv64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::Fnv64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func digestMurmurFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Digest::MurMurHash", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::MurMurHash", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::MurMurHash32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Digest::MurMurHash32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Digest::MurMurHash2A", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::MurMurHash2A", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::MurMurHash2A32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + { + Name: "Digest::MurMurHash2A32", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint32"}, + }, + } +} + +func digestCityFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Digest::CityHash", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::CityHash", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Digest::CityHash128", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/hyperscan.go b/internal/engine/ydb/lib/cpp/hyperscan.go new file mode 100644 index 0000000000..be3aa968e2 --- /dev/null +++ b/internal/engine/ydb/lib/cpp/hyperscan.go @@ -0,0 +1,105 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func HyperscanFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, hyperscanGrepFuncs()...) + funcs = append(funcs, hyperscanMatchFuncs()...) + funcs = append(funcs, hyperscanBacktrackingFuncs()...) + funcs = append(funcs, hyperscanMultiFuncs()...) + funcs = append(funcs, hyperscanCaptureFuncs()...) + funcs = append(funcs, hyperscanReplaceFuncs()...) + + return funcs +} + +func hyperscanGrepFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Hyperscan::Grep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func hyperscanMatchFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Hyperscan::Match", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func hyperscanBacktrackingFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Hyperscan::BacktrackingGrep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Hyperscan::BacktrackingMatch", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func hyperscanMultiFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Hyperscan::MultiGrep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Hyperscan::MultiMatch", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func hyperscanCaptureFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Hyperscan::Capture", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func hyperscanReplaceFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Hyperscan::Replace", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/ip.go b/internal/engine/ydb/lib/cpp/ip.go new file mode 100644 index 0000000000..a644da910c --- /dev/null +++ b/internal/engine/ydb/lib/cpp/ip.go @@ -0,0 +1,140 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func IpFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, ipFromStringFuncs()...) + funcs = append(funcs, ipToStringFuncs()...) + funcs = append(funcs, ipCheckFuncs()...) + funcs = append(funcs, ipConvertFuncs()...) + funcs = append(funcs, ipSubnetFuncs()...) + funcs = append(funcs, ipMatchFuncs()...) + + return funcs +} + +func ipFromStringFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Ip::FromString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Ip::SubnetFromString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func ipToStringFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Ip::ToString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Ip::ToString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func ipCheckFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Ip::IsIPv4", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Ip::IsIPv6", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Ip::IsEmbeddedIPv4", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func ipConvertFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Ip::ConvertToIPv6", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func ipSubnetFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Ip::GetSubnet", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Ip::GetSubnet", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint8"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Ip::GetSubnetByMask", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func ipMatchFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Ip::SubnetMatch", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/math.go b/internal/engine/ydb/lib/cpp/math.go new file mode 100644 index 0000000000..288464ad0d --- /dev/null +++ b/internal/engine/ydb/lib/cpp/math.go @@ -0,0 +1,439 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func MathFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, mathConstantsFuncs()...) + funcs = append(funcs, mathCheckFuncs()...) + funcs = append(funcs, mathUnaryFuncs()...) + funcs = append(funcs, mathBinaryFuncs()...) + funcs = append(funcs, mathLdexpFuncs()...) + funcs = append(funcs, mathRoundFuncs()...) + funcs = append(funcs, mathFuzzyEqualsFuncs()...) + funcs = append(funcs, mathModRemFuncs()...) + funcs = append(funcs, mathRoundingModeFuncs()...) + funcs = append(funcs, mathNearbyIntFuncs()...) + + return funcs +} + +func mathConstantsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::Pi", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::E", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Eps", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + } +} + +func mathCheckFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::IsInf", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Math::IsNaN", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Math::IsFinite", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func mathUnaryFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::Abs", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Acos", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Asin", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Asinh", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Atan", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Cbrt", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Ceil", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Cos", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Cosh", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Erf", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::ErfInv", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::ErfcInv", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Exp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Exp2", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Fabs", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Floor", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Lgamma", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Rint", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Sigmoid", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Sin", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Sinh", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Sqrt", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Tan", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Tanh", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Tgamma", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Trunc", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Log", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Log2", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Log10", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + } +} + +func mathBinaryFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::Atan2", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Fmod", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Hypot", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Pow", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Remainder", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + } +} + +func mathLdexpFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::Ldexp", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Int32"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + } +} + +func mathRoundFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::Round", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + { + Name: "Math::Round", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Int32"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + } +} + +func mathFuzzyEqualsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::FuzzyEquals", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Math::FuzzyEquals", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "Double"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func mathModRemFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::Mod", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Int64"}, + ReturnTypeNullable: true, + }, + { + Name: "Math::Rem", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Int64"}}, + {Type: &ast.TypeName{Name: "Int64"}}, + }, + ReturnType: &ast.TypeName{Name: "Int64"}, + ReturnTypeNullable: true, + }, + } +} + +func mathRoundingModeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::RoundDownward", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Math::RoundToNearest", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Math::RoundTowardZero", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Math::RoundUpward", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func mathNearbyIntFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Math::NearbyInt", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Double"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Int64"}, + ReturnTypeNullable: true, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/pcre.go b/internal/engine/ydb/lib/cpp/pcre.go new file mode 100644 index 0000000000..4b313ff80f --- /dev/null +++ b/internal/engine/ydb/lib/cpp/pcre.go @@ -0,0 +1,105 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func PcreFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, pcreGrepFuncs()...) + funcs = append(funcs, pcreMatchFuncs()...) + funcs = append(funcs, pcreBacktrackingFuncs()...) + funcs = append(funcs, pcreMultiFuncs()...) + funcs = append(funcs, pcreCaptureFuncs()...) + funcs = append(funcs, pcreReplaceFuncs()...) + + return funcs +} + +func pcreGrepFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pcre::Grep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pcreMatchFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pcre::Match", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pcreBacktrackingFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pcre::BacktrackingGrep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Pcre::BacktrackingMatch", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pcreMultiFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pcre::MultiGrep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Pcre::MultiMatch", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pcreCaptureFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pcre::Capture", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pcreReplaceFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pcre::Replace", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/pire.go b/internal/engine/ydb/lib/cpp/pire.go new file mode 100644 index 0000000000..ae7eece256 --- /dev/null +++ b/internal/engine/ydb/lib/cpp/pire.go @@ -0,0 +1,85 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func PireFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, pireGrepFuncs()...) + funcs = append(funcs, pireMatchFuncs()...) + funcs = append(funcs, pireMultiFuncs()...) + funcs = append(funcs, pireCaptureFuncs()...) + funcs = append(funcs, pireReplaceFuncs()...) + + return funcs +} + +func pireGrepFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pire::Grep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pireMatchFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pire::Match", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pireMultiFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pire::MultiGrep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Pire::MultiMatch", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pireCaptureFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pire::Capture", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func pireReplaceFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Pire::Replace", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/re2.go b/internal/engine/ydb/lib/cpp/re2.go new file mode 100644 index 0000000000..667c0f57e0 --- /dev/null +++ b/internal/engine/ydb/lib/cpp/re2.go @@ -0,0 +1,319 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func Re2Functions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, re2GrepFuncs()...) + funcs = append(funcs, re2MatchFuncs()...) + funcs = append(funcs, re2CaptureFuncs()...) + funcs = append(funcs, re2FindAndConsumeFuncs()...) + funcs = append(funcs, re2ReplaceFuncs()...) + funcs = append(funcs, re2CountFuncs()...) + funcs = append(funcs, re2OptionsFuncs()...) + + return funcs +} + +func re2GrepFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Re2::Grep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Grep", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func re2MatchFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Re2::Match", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Match", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func re2CaptureFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Re2::Capture", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Capture", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func re2FindAndConsumeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Re2::FindAndConsume", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::FindAndConsume", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func re2ReplaceFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Re2::Replace", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Replace", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func re2CountFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Re2::Count", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Count", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func re2OptionsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Re2::Options", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Re2::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/string.go b/internal/engine/ydb/lib/cpp/string.go new file mode 100644 index 0000000000..291dd10eec --- /dev/null +++ b/internal/engine/ydb/lib/cpp/string.go @@ -0,0 +1,152 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func StringFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, stringBase32Funcs()...) + funcs = append(funcs, stringBase64Funcs()...) + funcs = append(funcs, stringEscapeFuncs()...) + funcs = append(funcs, stringHexFuncs()...) + funcs = append(funcs, stringHtmlFuncs()...) + funcs = append(funcs, stringCgiFuncs()...) + + return funcs +} + +func stringBase32Funcs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "String::Base32Encode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "String::Base32Decode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "String::Base32StrictDecode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func stringBase64Funcs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "String::Base64Encode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "String::Base64Decode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "String::Base64StrictDecode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func stringEscapeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "String::EscapeC", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "String::UnescapeC", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func stringHexFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "String::HexEncode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "String::HexDecode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func stringHtmlFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "String::EncodeHtml", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "String::DecodeHtml", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func stringCgiFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "String::CgiEscape", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "String::CgiUnescape", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/unicode.go b/internal/engine/ydb/lib/cpp/unicode.go new file mode 100644 index 0000000000..e8c967020d --- /dev/null +++ b/internal/engine/ydb/lib/cpp/unicode.go @@ -0,0 +1,532 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func UnicodeFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, unicodeCheckFuncs()...) + funcs = append(funcs, unicodeLengthFuncs()...) + funcs = append(funcs, unicodeFindFuncs()...) + funcs = append(funcs, unicodeSubstringFuncs()...) + funcs = append(funcs, unicodeNormalizeFuncs()...) + funcs = append(funcs, unicodeTranslitFuncs()...) + funcs = append(funcs, unicodeLevensteinFuncs()...) + funcs = append(funcs, unicodeFoldFuncs()...) + funcs = append(funcs, unicodeReplaceFuncs()...) + funcs = append(funcs, unicodeRemoveFuncs()...) + funcs = append(funcs, unicodeCodePointFuncs()...) + funcs = append(funcs, unicodeReverseFuncs()...) + funcs = append(funcs, unicodeCaseFuncs()...) + funcs = append(funcs, unicodeSplitJoinFuncs()...) + funcs = append(funcs, unicodeToUint64Funcs()...) + funcs = append(funcs, unicodeStripFuncs()...) + funcs = append(funcs, unicodeIsFuncs()...) + funcs = append(funcs, unicodeIsUnicodeSetFuncs()...) + + return funcs +} + +func unicodeCheckFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::IsUtf", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func unicodeLengthFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::GetLength", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func unicodeFindFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::Find", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "Unicode::Find", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "Unicode::RFind", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "Unicode::RFind", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + } +} + +func unicodeSubstringFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::Substring", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeNormalizeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::Normalize", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::NormalizeNFD", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::NormalizeNFC", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::NormalizeNFKD", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::NormalizeNFKC", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeTranslitFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::Translit", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::Translit", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeLevensteinFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::LevensteinDistance", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func unicodeFoldFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::Fold", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::Fold", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::Fold", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::Fold", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::Fold", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::Fold", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeReplaceFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::ReplaceAll", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::ReplaceFirst", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::ReplaceLast", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeRemoveFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::RemoveAll", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::RemoveFirst", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::RemoveLast", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeCodePointFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::ToCodePointList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Unicode::FromCodePointList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeReverseFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::Reverse", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeCaseFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::ToLower", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::ToUpper", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + { + Name: "Unicode::ToTitle", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeSplitJoinFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::SplitToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Unicode::SplitToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Unicode::SplitToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Unicode::SplitToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Unicode::JoinFromList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeToUint64Funcs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::ToUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Unicode::ToUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Uint16"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Unicode::TryToUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "Unicode::TryToUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Uint16"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + } +} + +func unicodeStripFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::Strip", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Utf8"}, + }, + } +} + +func unicodeIsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::IsAscii", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Unicode::IsSpace", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Unicode::IsUpper", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Unicode::IsLower", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Unicode::IsAlpha", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Unicode::IsAlnum", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Unicode::IsHex", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func unicodeIsUnicodeSetFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Unicode::IsUnicodeSet", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Utf8"}}, + {Type: &ast.TypeName{Name: "Utf8"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/url.go b/internal/engine/ydb/lib/cpp/url.go new file mode 100644 index 0000000000..151115a8f0 --- /dev/null +++ b/internal/engine/ydb/lib/cpp/url.go @@ -0,0 +1,413 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func UrlFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, urlNormalizeFuncs()...) + funcs = append(funcs, urlEncodeDecodeFuncs()...) + funcs = append(funcs, urlParseFuncs()...) + funcs = append(funcs, urlGetFuncs()...) + funcs = append(funcs, urlDomainFuncs()...) + funcs = append(funcs, urlCutFuncs()...) + funcs = append(funcs, urlPunycodeFuncs()...) + funcs = append(funcs, urlQueryStringFuncs()...) + + return funcs +} + +func urlNormalizeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Url::Normalize", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::NormalizeWithDefaultHttpScheme", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func urlEncodeDecodeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Url::Encode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::Decode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func urlParseFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Url::Parse", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func urlGetFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Url::GetScheme", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Url::GetHost", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetHostPort", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetSchemeHost", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetSchemeHostPort", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetPort", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetTail", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetPath", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetFragment", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetCGIParam", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::GetDomain", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Uint8"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + } +} + +func urlDomainFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Url::GetTLD", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Url::IsKnownTLD", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Url::IsWellKnownTLD", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Url::GetDomainLevel", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "Url::GetSignificantDomain", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Url::GetSignificantDomain", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Url::GetOwner", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func urlCutFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Url::CutScheme", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::CutWWW", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::CutWWW2", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::CutQueryStringAndFragment", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} + +func urlPunycodeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Url::HostNameToPunycode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::ForceHostNameToPunycode", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Url::PunycodeToHostName", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Url::ForcePunycodeToHostName", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Url::CanBePunycodeHostName", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func urlQueryStringFuncs() []*catalog.Function { + // fixme: rewrite with containers if possible + return []*catalog.Function{ + { + Name: "Url::QueryStringToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::QueryStringToDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Url::BuildQueryString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + { + Name: "Url::BuildQueryString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + }, + } +} diff --git a/internal/engine/ydb/lib/cpp/yson.go b/internal/engine/ydb/lib/cpp/yson.go new file mode 100644 index 0000000000..78332e0f29 --- /dev/null +++ b/internal/engine/ydb/lib/cpp/yson.go @@ -0,0 +1,632 @@ +package cpp + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func YsonFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, ysonParseFuncs()...) + funcs = append(funcs, ysonFromFuncs()...) + funcs = append(funcs, ysonWithAttributesFuncs()...) + funcs = append(funcs, ysonEqualsFuncs()...) + funcs = append(funcs, ysonGetHashFuncs()...) + funcs = append(funcs, ysonIsFuncs()...) + funcs = append(funcs, ysonGetLengthFuncs()...) + funcs = append(funcs, ysonConvertToFuncs()...) + funcs = append(funcs, ysonConvertToListFuncs()...) + funcs = append(funcs, ysonConvertToDictFuncs()...) + funcs = append(funcs, ysonContainsFuncs()...) + funcs = append(funcs, ysonLookupFuncs()...) + funcs = append(funcs, ysonYPathFuncs()...) + funcs = append(funcs, ysonAttributesFuncs()...) + funcs = append(funcs, ysonSerializeFuncs()...) + funcs = append(funcs, ysonOptionsFuncs()...) + + return funcs +} + +func ysonParseFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::Parse", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Yson"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ParseJson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Json"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ParseJsonDecodeUtf8", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Json"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::Parse", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::ParseJson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::ParseJsonDecodeUtf8", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func ysonFromFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::From", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func ysonWithAttributesFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::WithAttributes", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func ysonEqualsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::Equals", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func ysonGetHashFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::GetHash", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func ysonIsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::IsEntity", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Yson::IsString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Yson::IsDouble", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Yson::IsUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Yson::IsInt64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Yson::IsBool", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Yson::IsList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + { + Name: "Yson::IsDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + }, + } +} + +func ysonGetLengthFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::GetLength", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + } +} + +func ysonConvertToFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::ConvertTo", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToBool", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::ConvertToInt64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Int64"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::ConvertToUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::ConvertToDouble", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::ConvertToString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::ConvertToList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func ysonConvertToListFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::ConvertToBoolList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToInt64List", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToUint64List", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToDoubleList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToStringList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func ysonConvertToDictFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::ConvertToDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToBoolDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToInt64Dict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToUint64Dict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToDoubleDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::ConvertToStringDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func ysonContainsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::Contains", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + ReturnTypeNullable: true, + }, + } +} + +func ysonLookupFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::Lookup", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::LookupBool", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::LookupInt64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Int64"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::LookupUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::LookupDouble", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::LookupString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::LookupDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::LookupList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func ysonYPathFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::YPath", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::YPathBool", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Bool"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::YPathInt64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Int64"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::YPathUint64", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::YPathDouble", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::YPathString", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "String"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::YPathDict", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::YPathList", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "String"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func ysonAttributesFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::Attributes", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} + +func ysonSerializeFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::Serialize", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Yson"}, + }, + { + Name: "Yson::SerializeText", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Yson"}, + }, + { + Name: "Yson::SerializePretty", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Yson"}, + }, + { + Name: "Yson::SerializeJson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Json"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::SerializeJson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Json"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::SerializeJson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Json"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::SerializeJson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Json"}, + ReturnTypeNullable: true, + }, + { + Name: "Yson::SerializeJson", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "Json"}, + ReturnTypeNullable: true, + }, + } +} + +func ysonOptionsFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "Yson::Options", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + { + Name: "Yson::Options", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Bool"}}, + {Type: &ast.TypeName{Name: "Bool"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} diff --git a/internal/engine/ydb/lib/window.go b/internal/engine/ydb/lib/window.go new file mode 100644 index 0000000000..7c339217ad --- /dev/null +++ b/internal/engine/ydb/lib/window.go @@ -0,0 +1,163 @@ +package lib + +import ( + "github.com/sqlc-dev/sqlc/internal/sql/ast" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func WindowFunctions() []*catalog.Function { + var funcs []*catalog.Function + + funcs = append(funcs, rowNumberFuncs()...) + funcs = append(funcs, lagLeadFuncs()...) + funcs = append(funcs, firstLastValueFuncs()...) + funcs = append(funcs, nthValueFuncs()...) + funcs = append(funcs, rankFuncs()...) + funcs = append(funcs, ntileFuncs()...) + funcs = append(funcs, cumeDistFuncs()...) + funcs = append(funcs, sessionStartFuncs()...) + + return funcs +} + +func rowNumberFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "ROW_NUMBER", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func lagLeadFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "LAG", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "LAG", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "LEAD", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "LEAD", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "Uint32"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func firstLastValueFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "FIRST_VALUE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + { + Name: "LAST_VALUE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func nthValueFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "NTH_VALUE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "any"}, + ReturnTypeNullable: true, + }, + } +} + +func rankFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "RANK", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "DENSE_RANK", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + { + Name: "PERCENT_RANK", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "any"}}, + }, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + } +} + +func ntileFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "NTILE", + Args: []*catalog.Argument{ + {Type: &ast.TypeName{Name: "Uint64"}}, + }, + ReturnType: &ast.TypeName{Name: "Uint64"}, + }, + } +} + +func cumeDistFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "CUME_DIST", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "Double"}, + }, + } +} + +func sessionStartFuncs() []*catalog.Function { + return []*catalog.Function{ + { + Name: "SESSION_START", + Args: []*catalog.Argument{}, + ReturnType: &ast.TypeName{Name: "any"}, + }, + } +} diff --git a/internal/engine/ydb/parse.go b/internal/engine/ydb/parse.go new file mode 100755 index 0000000000..8fbdd81ebb --- /dev/null +++ b/internal/engine/ydb/parse.go @@ -0,0 +1,97 @@ +package ydb + +import ( + "errors" + "fmt" + "io" + + "github.com/antlr4-go/antlr/v4" + "github.com/sqlc-dev/sqlc/internal/source" + "github.com/sqlc-dev/sqlc/internal/sql/ast" + parser "github.com/ydb-platform/yql-parsers/go" +) + +type errorListener struct { + *antlr.DefaultErrorListener + + err string +} + +func (el *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) { + el.err = msg +} + +// func (el *errorListener) ReportAmbiguity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, exact bool, ambigAlts *antlr.BitSet, configs antlr.ATNConfigSet) { +// } +// +// func (el *errorListener) ReportAttemptingFullContext(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, conflictingAlts *antlr.BitSet, configs antlr.ATNConfigSet) { +// } +// +// func (el *errorListener) ReportContextSensitivity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex, prediction int, configs antlr.ATNConfigSet) { +// } + +func NewParser() *Parser { + return &Parser{} +} + +type Parser struct { +} + +func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) { + blob, err := io.ReadAll(r) + if err != nil { + return nil, err + } + content := string(blob) + input := antlr.NewInputStream(content) + lexer := parser.NewYQLLexer(input) + stream := antlr.NewCommonTokenStream(lexer, 0) + pp := parser.NewYQLParser(stream) + el := &errorListener{} + pp.AddErrorListener(el) + // pp.BuildParseTrees = true + tree := pp.Sql_query() + if el.err != "" { + return nil, errors.New(el.err) + } + pctx, ok := tree.(*parser.Sql_queryContext) + if !ok { + return nil, fmt.Errorf("expected ParserContext; got %T\n ", tree) + } + var stmts []ast.Statement + stmtListCtx := pctx.Sql_stmt_list() + if stmtListCtx != nil { + loc := 0 + for _, stmt := range stmtListCtx.AllSql_stmt() { + converter := &cc{content: string(blob)} + out, ok := stmt.Accept(converter).(ast.Node) + if !ok { + return nil, fmt.Errorf("expected ast.Node; got %T", out) + } + if _, ok := out.(*ast.TODO); ok { + loc = byteOffset(content, stmt.GetStop().GetStop() + 2) + continue + } + if out != nil { + len := byteOffset(content, stmt.GetStop().GetStop() + 1) - loc + stmts = append(stmts, ast.Statement{ + Raw: &ast.RawStmt{ + Stmt: out, + StmtLocation: loc, + StmtLen: len, + }, + }) + loc = byteOffset(content, stmt.GetStop().GetStop() + 2) + } + } + } + return stmts, nil +} + +func (p *Parser) CommentSyntax() source.CommentSyntax { + return source.CommentSyntax{ + Dash: true, + Hash: false, + SlashStar: true, + } +} diff --git a/internal/engine/ydb/reserved.go b/internal/engine/ydb/reserved.go new file mode 100644 index 0000000000..8db504c0b9 --- /dev/null +++ b/internal/engine/ydb/reserved.go @@ -0,0 +1,301 @@ +package ydb + +import "strings" + + +func (p *Parser) IsReservedKeyword(s string) bool { + switch strings.ToLower(s) { + case "abort": + case "action": + case "add": + case "after": + case "all": + case "alter": + case "analyze": + case "and": + case "ansi": + case "any": + case "array": + case "as": + case "asc": + case "assume": + case "asymmetric": + case "async": + case "at": + case "attach": + case "attributes": + case "autoincrement": + case "automap": + case "backup": + case "batch": + case "collection": + case "before": + case "begin": + case "bernoulli": + case "between": + case "bitcast": + case "by": + case "callable": + case "cascade": + case "case": + case "cast": + case "changefeed": + case "check": + case "classifier": + case "collate": + case "column": + case "columns": + case "commit": + case "compact": + case "conditional": + case "conflict": + case "connect": + case "constraint": + case "consumer": + case "cover": + case "create": + case "cross": + case "cube": + case "current": + case "current_date": + case "current_time": + case "current_timestamp": + case "data": + case "database": + case "decimal": + case "declare": + case "default": + case "deferrable": + case "deferred": + case "define": + case "delete": + case "desc": + case "describe": + case "detach": + case "dict": + case "directory": + case "disable": + case "discard": + case "distinct": + case "do": + case "drop": + case "each": + case "else": + case "empty": + case "empty_action": + case "encrypted": + case "end": + case "enum": + case "erase": + case "error": + case "escape": + case "evaluate": + case "except": + case "exclude": + case "exclusion": + case "exclusive": + case "exists": + case "explain": + case "export": + case "external": + case "fail": + case "false": + case "family": + case "filter": + case "first": + case "flatten": + case "flow": + case "following": + case "for": + case "foreign": + case "from": + case "full": + case "function": + case "glob": + case "global": + case "grant": + case "group": + case "grouping": + case "groups": + case "hash": + case "having": + case "hop": + case "if": + case "ignore": + case "ilike": + case "immediate": + case "import": + case "in": + case "increment": + case "incremental": + case "index": + case "indexed": + case "inherits": + case "initial": + case "initially": + case "inner": + case "insert": + case "instead": + case "intersect": + case "into": + case "is": + case "isnull": + case "join": + case "json_exists": + case "json_query": + case "json_value": + case "key": + case "last": + case "left": + case "legacy": + case "like": + case "limit": + case "list": + case "local": + case "login": + case "manage": + case "match": + case "matches": + case "match_recognize": + case "measures": + case "microseconds": + case "milliseconds": + case "modify": + case "nanoseconds": + case "natural": + case "next": + case "no": + case "nologin": + case "not": + case "notnull": + case "null": + case "nulls": + case "object": + case "of": + case "offset": + case "omit": + case "on": + case "one": + case "only": + case "option": + case "optional": + case "or": + case "order": + case "others": + case "outer": + case "over": + case "owner": + case "parallel": + case "partition": + case "passing": + case "password": + case "past": + case "pattern": + case "per": + case "permute": + case "plan": + case "pool": + case "pragma": + case "preceding": + case "presort": + case "primary": + case "privileges": + case "process": + case "query": + case "queue": + case "raise": + case "range": + case "reduce": + case "references": + case "regexp": + case "reindex": + case "release": + case "remove": + case "rename": + case "repeatable": + case "replace": + case "replication": + case "reset": + case "resource": + case "respect": + case "restart": + case "restore": + case "restrict": + case "result": + case "return": + case "returning": + case "revert": + case "revoke": + case "right": + case "rlike": + case "rollback": + case "rollup": + case "row": + case "rows": + case "sample": + case "savepoint": + case "schema": + case "seconds": + case "seek": + case "select": + case "semi": + case "set": + case "sets": + case "show": + case "tskip": + case "sequence": + case "source": + case "start": + case "stream": + case "struct": + case "subquery": + case "subset": + case "symbols": + case "symmetric": + case "sync": + case "system": + case "table": + case "tables": + case "tablesample": + case "tablestore": + case "tagged": + case "temp": + case "temporary": + case "then": + case "ties": + case "to": + case "topic": + case "transaction": + case "transfer": + case "trigger": + case "true": + case "tuple": + case "type": + case "unbounded": + case "unconditional": + case "union": + case "unique": + case "unknown": + case "unmatched": + case "update": + case "upsert": + case "use": + case "user": + case "using": + case "vacuum": + case "values": + case "variant": + case "view": + case "virtual": + case "when": + case "where": + case "window": + case "with": + case "without": + case "wrapper": + case "xor": + default: + return false + } + return true +} diff --git a/internal/engine/ydb/stdlib.go b/internal/engine/ydb/stdlib.go new file mode 100644 index 0000000000..69ef6b223e --- /dev/null +++ b/internal/engine/ydb/stdlib.go @@ -0,0 +1,22 @@ +package ydb + +import ( + "github.com/sqlc-dev/sqlc/internal/engine/ydb/lib" + "github.com/sqlc-dev/sqlc/internal/sql/catalog" +) + +func defaultSchema(name string) *catalog.Schema { + s := &catalog.Schema{ + Name: name, + Funcs: []*catalog.Function{}, + } + + s.Funcs = append(s.Funcs, lib.BasicFunctions()...) + s.Funcs = append(s.Funcs, lib.AggregateFunctions()...) + s.Funcs = append(s.Funcs, lib.WindowFunctions()...) + s.Funcs = append(s.Funcs, lib.CppFunctions()...) + + // TODO: add container functions if + + return s +} diff --git a/internal/engine/ydb/utils.go b/internal/engine/ydb/utils.go new file mode 100755 index 0000000000..8f118df09b --- /dev/null +++ b/internal/engine/ydb/utils.go @@ -0,0 +1,297 @@ +package ydb + +import ( + "strconv" + "strings" + "unicode/utf8" + + "github.com/antlr4-go/antlr/v4" + "github.com/sqlc-dev/sqlc/internal/sql/ast" + parser "github.com/ydb-platform/yql-parsers/go" +) + +type objectRefProvider interface { + antlr.ParserRuleContext + Object_ref() parser.IObject_refContext +} + +func parseTableName(ctx objectRefProvider) *ast.TableName { + return parseObjectRef(ctx.Object_ref()) +} + +func parseObjectRef(r parser.IObject_refContext) *ast.TableName { + if r == nil { + return nil + } + ref := r.(*parser.Object_refContext) + + parts := []string{} + + if cl := ref.Cluster_expr(); cl != nil { + parts = append(parts, parseClusterExpr(cl)) + } + + if idOrAt := ref.Id_or_at(); idOrAt != nil { + parts = append(parts, parseIdOrAt(idOrAt)) + } + + objectName := strings.Join(parts, ".") + + return &ast.TableName{ + Schema: "", + Name: identifier(objectName), + } +} + +func parseClusterExpr(ctx parser.ICluster_exprContext) string { + if ctx == nil { + return "" + } + return identifier(ctx.GetText()) +} + +func parseIdOrAt(ctx parser.IId_or_atContext) string { + if ctx == nil { + return "" + } + idOrAt := ctx.(*parser.Id_or_atContext) + + if ao := idOrAt.An_id_or_type(); ao != nil { + return identifier(parseAnIdOrType(ao)) + } + return "" +} + +func parseAnIdOrType(ctx parser.IAn_id_or_typeContext) string { + if ctx == nil { + return "" + } + anId := ctx.(*parser.An_id_or_typeContext) + + if anId.Id_or_type() != nil { + return identifier(parseIdOrType(anId.Id_or_type())) + } + + if anId.STRING_VALUE() != nil { + return identifier(anId.STRING_VALUE().GetText()) + } + + return "" +} + +func parseIdOrType(ctx parser.IId_or_typeContext) string { + if ctx == nil { + return "" + } + Id := ctx.(*parser.Id_or_typeContext) + if Id.Id() != nil { + return identifier(parseId(Id.Id())) + } + + return "" +} + +func parseAnId(ctx parser.IAn_idContext) string { + if id := ctx.Id(); id != nil { + return id.GetText() + } else if str := ctx.STRING_VALUE(); str != nil { + return str.GetText() + } + return "" +} + +func parseAnIdSchema(ctx parser.IAn_id_schemaContext) string { + if ctx == nil { + return "" + } + if id := ctx.Id_schema(); id != nil { + return id.GetText() + } else if str := ctx.STRING_VALUE(); str != nil { + return str.GetText() + } + return "" +} + +func parseId(ctx parser.IIdContext) string { + if ctx == nil { + return "" + } + return ctx.GetText() +} + +func parseAnIdTable(ctx parser.IAn_id_tableContext) string { + if ctx == nil { + return "" + } + if id := ctx.Id_table(); id != nil { + return id.GetText() + } else if str := ctx.STRING_VALUE(); str != nil { + return str.GetText() + } + return "" +} + +func parseIntegerValue(text string) (int64, error) { + text = strings.ToLower(text) + base := 10 + + switch { + case strings.HasPrefix(text, "0x"): + base = 16 + text = strings.TrimPrefix(text, "0x") + + case strings.HasPrefix(text, "0o"): + base = 8 + text = strings.TrimPrefix(text, "0o") + + case strings.HasPrefix(text, "0b"): + base = 2 + text = strings.TrimPrefix(text, "0b") + } + + // debug!!! + text = strings.TrimRight(text, "pulstibn") + + return strconv.ParseInt(text, base, 64) +} + +func (c *cc) extractRoleSpec(n parser.IRole_nameContext, roletype ast.RoleSpecType) (*ast.RoleSpec, bool, ast.Node) { + if n == nil { + return nil, false, nil + } + roleNode, ok := n.Accept(c).(ast.Node) + if !ok { + return nil, false, nil + } + + roleSpec := &ast.RoleSpec{ + Roletype: roletype, + Location: n.GetStart().GetStart(), + } + + isParam := true + switch v := roleNode.(type) { + case *ast.A_Const: + switch val := v.Val.(type) { + case *ast.String: + roleSpec.Rolename = &val.Str + isParam = false + case *ast.Boolean: + roleSpec.BindRolename = roleNode + default: + return nil, false, nil + } + case *ast.ParamRef, *ast.A_Expr: + roleSpec.BindRolename = roleNode + default: + return nil, false, nil + } + + return roleSpec, isParam, roleNode +} + +func byteOffset(s string, runeIndex int) int { + count := 0 + for i := range s { + if count == runeIndex { + return i + } + count++ + } + return len(s) +} + +func byteOffsetFromRuneIndex(s string, runeIndex int) int { + if runeIndex <= 0 { + return 0 + } + bytePos := 0 + for i := 0; i < runeIndex && bytePos < len(s); i++ { + _, size := utf8.DecodeRuneInString(s[bytePos:]) + bytePos += size + } + return bytePos +} + +func emptySelectStmt() *ast.SelectStmt { + return &ast.SelectStmt{ + DistinctClause: &ast.List{}, + TargetList: &ast.List{}, + FromClause: &ast.List{}, + GroupClause: &ast.List{}, + WindowClause: &ast.List{}, + ValuesLists: &ast.List{}, + SortClause: &ast.List{}, + LockingClause: &ast.List{}, + } +} + +func (c *cc) collectComparisonOps(n parser.IEq_subexprContext) []antlr.TerminalNode { + var ops []antlr.TerminalNode + for _, child := range n.GetChildren() { + if tn, ok := child.(antlr.TerminalNode); ok { + switch tn.GetText() { + case "<", "<=", ">", ">=": + ops = append(ops, tn) + } + } + } + return ops +} + +func (c *cc) collectBitwiseOps(ctx parser.INeq_subexprContext) []antlr.TerminalNode { + var ops []antlr.TerminalNode + children := ctx.GetChildren() + for _, child := range children { + if tn, ok := child.(antlr.TerminalNode); ok { + txt := tn.GetText() + switch txt { + case "<<", ">>", "<<|", ">>|", "&", "|", "^": + ops = append(ops, tn) + } + } + } + return ops +} + +func (c *cc) collectBitOps(ctx parser.IBit_subexprContext) []antlr.TerminalNode { + var ops []antlr.TerminalNode + children := ctx.GetChildren() + for _, child := range children { + if tn, ok := child.(antlr.TerminalNode); ok { + txt := tn.GetText() + switch txt { + case "+", "-": + ops = append(ops, tn) + } + } + } + return ops +} + +func (c *cc) collectAddOps(ctx parser.IAdd_subexprContext) []antlr.TerminalNode { + var ops []antlr.TerminalNode + for _, child := range ctx.GetChildren() { + if tn, ok := child.(antlr.TerminalNode); ok { + switch tn.GetText() { + case "*", "/", "%": + ops = append(ops, tn) + } + } + } + return ops +} + +func (c *cc) collectEqualityOps(ctx parser.ICond_exprContext) []antlr.TerminalNode { + var ops []antlr.TerminalNode + children := ctx.GetChildren() + for _, child := range children { + if tn, ok := child.(antlr.TerminalNode); ok { + switch tn.GetText() { + case "=", "==", "!=", "<>": + ops = append(ops, tn) + } + } + } + return ops +} diff --git a/internal/sql/ast/create_role_stmt.go b/internal/sql/ast/create_role_stmt.go index 144540863e..44565fb64c 100644 --- a/internal/sql/ast/create_role_stmt.go +++ b/internal/sql/ast/create_role_stmt.go @@ -4,6 +4,9 @@ type CreateRoleStmt struct { StmtType RoleStmtType Role *string Options *List + + // YDB specific + BindRole Node } func (n *CreateRoleStmt) Pos() int { diff --git a/internal/sql/ast/delete_stmt.go b/internal/sql/ast/delete_stmt.go index d77f043a12..715f976ae6 100644 --- a/internal/sql/ast/delete_stmt.go +++ b/internal/sql/ast/delete_stmt.go @@ -1,12 +1,18 @@ package ast type DeleteStmt struct { - Relations *List - UsingClause *List - WhereClause Node - LimitCount Node + Relations *List + UsingClause *List + WhereClause Node + LimitCount Node + ReturningList *List WithClause *WithClause + + // YDB specific + Batch bool + OnCols *List + OnSelectStmt Node } func (n *DeleteStmt) Pos() int { @@ -22,6 +28,9 @@ func (n *DeleteStmt) Format(buf *TrackedBuffer) { buf.astFormat(n.WithClause) buf.WriteString(" ") } + if n.Batch { + buf.WriteString("BATCH ") + } buf.WriteString("DELETE FROM ") if items(n.Relations) { @@ -33,6 +42,20 @@ func (n *DeleteStmt) Format(buf *TrackedBuffer) { buf.astFormat(n.WhereClause) } + if items(n.OnCols) || set(n.OnSelectStmt) { + buf.WriteString(" ON ") + + if items(n.OnCols) { + buf.WriteString("(") + buf.astFormat(n.OnCols) + buf.WriteString(") ") + } + + if set(n.OnSelectStmt) { + buf.astFormat(n.OnSelectStmt) + } + } + if set(n.LimitCount) { buf.WriteString(" LIMIT ") buf.astFormat(n.LimitCount) diff --git a/internal/sql/ast/insert_stmt.go b/internal/sql/ast/insert_stmt.go index 3cdf854091..b3e7c60809 100644 --- a/internal/sql/ast/insert_stmt.go +++ b/internal/sql/ast/insert_stmt.go @@ -23,8 +23,24 @@ func (n *InsertStmt) Format(buf *TrackedBuffer) { buf.astFormat(n.WithClause) buf.WriteString(" ") } - - buf.WriteString("INSERT INTO ") + if n.OnConflictClause != nil { + switch n.OnConflictClause.Action { + case OnConflictAction_INSERT_OR_ABORT: + buf.WriteString("INSERT OR ABORT INTO ") + case OnConflictAction_INSERT_OR_REVERT: + buf.WriteString("INSERT OR REVERT INTO ") + case OnConflictAction_INSERT_OR_IGNORE: + buf.WriteString("INSERT OR IGNORE INTO ") + case OnConflictAction_UPSERT: + buf.WriteString("UPSERT INTO ") + case OnConflictAction_REPLACE: + buf.WriteString("REPLACE INTO ") + default: + buf.WriteString("INSERT INTO ") + } + } else { + buf.WriteString("INSERT INTO ") + } if n.Relation != nil { buf.astFormat(n.Relation) } @@ -38,7 +54,7 @@ func (n *InsertStmt) Format(buf *TrackedBuffer) { buf.astFormat(n.SelectStmt) } - if n.OnConflictClause != nil { + if n.OnConflictClause != nil && n.OnConflictClause.Action < 4 { buf.WriteString(" ON CONFLICT DO NOTHING ") } diff --git a/internal/sql/ast/on_conflict_action_type.go b/internal/sql/ast/on_conflict_action_type.go new file mode 100644 index 0000000000..c149fe8d04 --- /dev/null +++ b/internal/sql/ast/on_conflict_action_type.go @@ -0,0 +1,15 @@ +package ast + +const ( + OnConflictAction_ON_CONFLICT_ACTION_UNDEFINED OnConflictAction = 0 + OnConflictAction_ONCONFLICT_NONE OnConflictAction = 1 + OnConflictAction_ONCONFLICT_NOTHING OnConflictAction = 2 + OnConflictAction_ONCONFLICT_UPDATE OnConflictAction = 3 + + // YQL-specific + OnConflictAction_INSERT_OR_ABORT OnConflictAction = 4 + OnConflictAction_INSERT_OR_REVERT OnConflictAction = 5 + OnConflictAction_INSERT_OR_IGNORE OnConflictAction = 6 + OnConflictAction_UPSERT OnConflictAction = 7 + OnConflictAction_REPLACE OnConflictAction = 8 +) diff --git a/internal/sql/ast/param_ref.go b/internal/sql/ast/param_ref.go index 8bd724993d..6ffe8cc5f0 100644 --- a/internal/sql/ast/param_ref.go +++ b/internal/sql/ast/param_ref.go @@ -6,6 +6,9 @@ type ParamRef struct { Number int Location int Dollar bool + + // YDB specific + Plike bool } func (n *ParamRef) Pos() int { @@ -16,5 +19,9 @@ func (n *ParamRef) Format(buf *TrackedBuffer) { if n == nil { return } + if n.Plike { + fmt.Fprintf(buf, "$p%d", n.Number) + return + } fmt.Fprintf(buf, "$%d", n.Number) } diff --git a/internal/sql/ast/pragma_stmt.go b/internal/sql/ast/pragma_stmt.go new file mode 100644 index 0000000000..46dc40dbf5 --- /dev/null +++ b/internal/sql/ast/pragma_stmt.go @@ -0,0 +1,43 @@ +package ast + +// YDB specific +type Pragma_stmt struct { + Name Node + Cols *List + Equals bool + Values *List + Location int +} + +func (n *Pragma_stmt) Pos() int { + return n.Location +} + +func (n *Pragma_stmt) Format(buf *TrackedBuffer) { + if n == nil { + return + } + + buf.WriteString("PRAGMA ") + if n.Name != nil { + buf.astFormat(n.Name) + } + if n.Cols != nil { + buf.astFormat(n.Cols) + } + + if n.Equals { + buf.WriteString(" = ") + } + + if n.Values != nil { + if n.Equals { + buf.astFormat(n.Values) + } else { + buf.WriteString("(") + buf.astFormat(n.Values) + buf.WriteString(")") + } + } + +} diff --git a/internal/sql/ast/role_spec.go b/internal/sql/ast/role_spec.go index fba4cecf7d..5b7c871c54 100644 --- a/internal/sql/ast/role_spec.go +++ b/internal/sql/ast/role_spec.go @@ -4,6 +4,8 @@ type RoleSpec struct { Roletype RoleSpecType Rolename *string Location int + + BindRolename Node } func (n *RoleSpec) Pos() int { diff --git a/internal/sql/ast/update_stmt.go b/internal/sql/ast/update_stmt.go index efd496ad75..ea3e5bc65f 100644 --- a/internal/sql/ast/update_stmt.go +++ b/internal/sql/ast/update_stmt.go @@ -10,6 +10,11 @@ type UpdateStmt struct { LimitCount Node ReturningList *List WithClause *WithClause + + // YDB specific + Batch bool + OnCols *List + OnSelectStmt Node } func (n *UpdateStmt) Pos() int { @@ -25,6 +30,10 @@ func (n *UpdateStmt) Format(buf *TrackedBuffer) { buf.WriteString(" ") } + if n.Batch { + buf.WriteString("BATCH ") + } + buf.WriteString("UPDATE ") if items(n.Relations) { buf.astFormat(n.Relations) @@ -100,6 +109,20 @@ func (n *UpdateStmt) Format(buf *TrackedBuffer) { buf.astFormat(n.WhereClause) } + if items(n.OnCols) || set(n.OnSelectStmt) { + buf.WriteString(" ON ") + + if items(n.OnCols) { + buf.WriteString("(") + buf.astFormat(n.OnCols) + buf.WriteString(") ") + } + + if set(n.OnSelectStmt) { + buf.astFormat(n.OnSelectStmt) + } + } + if set(n.LimitCount) { buf.WriteString(" LIMIT ") buf.astFormat(n.LimitCount) diff --git a/internal/sql/ast/use_stmt.go b/internal/sql/ast/use_stmt.go new file mode 100644 index 0000000000..dee393c321 --- /dev/null +++ b/internal/sql/ast/use_stmt.go @@ -0,0 +1,11 @@ +package ast + +// YDB specific +type UseStmt struct { + Xpr Node + Location int +} + +func (n *UseStmt) Pos() int { + return n.Location +} diff --git a/internal/sql/astutils/rewrite.go b/internal/sql/astutils/rewrite.go index 93c5be3cfb..372d0ee7a2 100644 --- a/internal/sql/astutils/rewrite.go +++ b/internal/sql/astutils/rewrite.go @@ -605,6 +605,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. a.apply(n, "Params", nil, n.Params) case *ast.CreateRoleStmt: + a.apply(n, "BindRole", nil, n.BindRole) a.apply(n, "Options", nil, n.Options) case *ast.CreateSchemaStmt: @@ -685,6 +686,8 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. a.apply(n, "Relations", nil, n.Relations) a.apply(n, "UsingClause", nil, n.UsingClause) a.apply(n, "WhereClause", nil, n.WhereClause) + a.apply(n, "Cols", nil, n.OnCols) + a.apply(n, "SelectStmt", nil, n.OnSelectStmt) a.apply(n, "ReturningList", nil, n.ReturningList) a.apply(n, "WithClause", nil, n.WithClause) @@ -922,6 +925,11 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. case *ast.PartitionSpec: a.apply(n, "PartParams", nil, n.PartParams) + case *ast.Pragma_stmt: + a.apply(n, "Pragma", nil, n.Name) + a.apply(n, "Args", nil, n.Cols) + a.apply(n, "Options", nil, n.Values) + case *ast.PrepareStmt: a.apply(n, "Argtypes", nil, n.Argtypes) a.apply(n, "Query", nil, n.Query) @@ -1027,7 +1035,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. a.apply(n, "Val", nil, n.Val) case *ast.RoleSpec: - // pass + a.apply(n, "BindRolename", nil, n.BindRolename) case *ast.RowCompareExpr: a.apply(n, "Xpr", nil, n.Xpr) @@ -1159,9 +1167,14 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. a.apply(n, "TargetList", nil, n.TargetList) a.apply(n, "WhereClause", nil, n.WhereClause) a.apply(n, "FromClause", nil, n.FromClause) + a.apply(n, "Cols", nil, n.OnCols) + a.apply(n, "SelectStmt", nil, n.OnSelectStmt) a.apply(n, "ReturningList", nil, n.ReturningList) a.apply(n, "WithClause", nil, n.WithClause) + case *ast.UseStmt: + a.apply(n, "Xpr", nil, n.Xpr) + case *ast.VacuumStmt: a.apply(n, "Relation", nil, n.Relation) a.apply(n, "VaCols", nil, n.VaCols) diff --git a/internal/sql/astutils/walk.go b/internal/sql/astutils/walk.go index 0943379f03..e7b78d126b 100644 --- a/internal/sql/astutils/walk.go +++ b/internal/sql/astutils/walk.go @@ -898,6 +898,9 @@ func Walk(f Visitor, node ast.Node) { } case *ast.CreateRoleStmt: + if n.BindRole != nil { + Walk(f, n.BindRole) + } if n.Options != nil { Walk(f, n.Options) } @@ -1068,6 +1071,12 @@ func Walk(f Visitor, node ast.Node) { if n.WhereClause != nil { Walk(f, n.WhereClause) } + if n.OnCols != nil { + Walk(f, n.OnCols) + } + if n.OnSelectStmt != nil { + Walk(f, n.OnSelectStmt) + } if n.LimitCount != nil { Walk(f, n.LimitCount) } @@ -1510,6 +1519,17 @@ func Walk(f Visitor, node ast.Node) { Walk(f, n.PartParams) } + case *ast.Pragma_stmt: + if n.Name != nil { + Walk(f, n.Name) + } + if n.Cols != nil { + Walk(f, n.Cols) + } + if n.Values != nil { + Walk(f, n.Values) + } + case *ast.PrepareStmt: if n.Argtypes != nil { Walk(f, n.Argtypes) @@ -1752,7 +1772,9 @@ func Walk(f Visitor, node ast.Node) { } case *ast.RoleSpec: - // pass + if n.BindRolename != nil { + Walk(f, n.BindRolename) + } case *ast.RowCompareExpr: if n.Xpr != nil { @@ -2041,6 +2063,12 @@ func Walk(f Visitor, node ast.Node) { if n.FromClause != nil { Walk(f, n.FromClause) } + if n.OnCols != nil { + Walk(f, n.OnCols) + } + if n.OnSelectStmt != nil { + Walk(f, n.OnSelectStmt) + } if n.LimitCount != nil { Walk(f, n.LimitCount) } @@ -2051,6 +2079,11 @@ func Walk(f Visitor, node ast.Node) { Walk(f, n.WithClause) } + case *ast.UseStmt: + if n.Xpr != nil { + Walk(f, n.Xpr) + } + case *ast.VacuumStmt: if n.Relation != nil { Walk(f, n.Relation) diff --git a/internal/sql/rewrite/parameters.go b/internal/sql/rewrite/parameters.go index d1ea1a22cc..9146d17e08 100644 --- a/internal/sql/rewrite/parameters.go +++ b/internal/sql/rewrite/parameters.go @@ -172,6 +172,8 @@ func NamedParameters(engine config.Engine, raw *ast.RawStmt, numbs map[int]bool, replace = "?" } else if engine == config.EngineSQLite { replace = fmt.Sprintf("?%d", argn) + } else if engine == config.EngineYDB { + replace = fmt.Sprintf("$%s", paramName) } else { replace = fmt.Sprintf("$%d", argn) } diff --git a/internal/sqltest/local/ydb.go b/internal/sqltest/local/ydb.go new file mode 100644 index 0000000000..e3e51e3716 --- /dev/null +++ b/internal/sqltest/local/ydb.go @@ -0,0 +1,87 @@ +package local + +import ( + "context" + "fmt" + "math/rand" + "os" + "testing" + "time" + + migrate "github.com/sqlc-dev/sqlc/internal/migrations" + "github.com/sqlc-dev/sqlc/internal/sql/sqlpath" + "github.com/ydb-platform/ydb-go-sdk/v3" + "github.com/ydb-platform/ydb-go-sdk/v3/query" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func YDB(t *testing.T, migrations []string) *ydb.Driver { + return link_YDB(t, migrations, true, false) +} + +func YDBTLS(t *testing.T, migrations []string) *ydb.Driver { + return link_YDB(t, migrations, true, true) +} + +func ReadOnlyYDB(t *testing.T, migrations []string) *ydb.Driver { + return link_YDB(t, migrations, false, false) +} + +func ReadOnlyYDBTLS(t *testing.T, migrations []string) *ydb.Driver { + return link_YDB(t, migrations, false, true) +} + +func link_YDB(t *testing.T, migrations []string, rw bool, tls bool) *ydb.Driver { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + t.Helper() + + dbuiri := os.Getenv("YDB_SERVER_URI") + if dbuiri == "" { + t.Skip("YDB_SERVER_URI is empty") + } + + baseDB := os.Getenv("YDB_DATABASE") + if baseDB == "" { + baseDB = "/local" + } + + var connectionString string + if tls { + connectionString = fmt.Sprintf("grpcs://%s%s", dbuiri, baseDB) + } else { + connectionString = fmt.Sprintf("grpc://%s%s", dbuiri, baseDB) + } + + db, err := ydb.Open(ctx, connectionString, + ydb.WithInsecure(), + ydb.WithDiscoveryInterval(time.Hour), + ) + if err != nil { + t.Fatalf("failed to open YDB connection: %s", 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) + } + stmt := migrate.RemoveRollbackStatements(string(blob)) + + err = db.Query().Exec(ctx, stmt, query.WithTxControl(query.EmptyTxControl())) + if err != nil { + t.Fatalf("failed to apply migration: %s\nSQL: %s", err, stmt) + } + } + + return db +}