Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support full-text search DDLs #111

Merged
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fe57780
Implement CREATE/DROP/ALTER SEARCH INDEX
apstndb Sep 21, 2024
17228a1
Make STORED optional
apstndb Sep 21, 2024
a9c66b1
Support HIDDEN columns
apstndb Sep 21, 2024
3d8958c
Add TOKENLIST type
apstndb Sep 21, 2024
41d9fdc
Update testdata
apstndb Sep 21, 2024
403348a
Implement CREATE/DROP/ALTER SEARCH INDEX
apstndb Sep 21, 2024
6987a82
Add parser functions/methods
apstndb Oct 15, 2024
c159518
Add TOKENLIST as simple type
apstndb Oct 15, 2024
50c47ee
Fix HIDDEN implementation
apstndb Oct 15, 2024
d3d9be2
Update testdata
apstndb Oct 15, 2024
758b422
Fix ast of search index DDLs
apstndb Oct 15, 2024
48751bf
Fix some SQL()
apstndb Oct 15, 2024
f1c69e8
Add create_search_index_full.sql
apstndb Oct 15, 2024
51314c4
Implement SearchIndexOptions on GenericOptions
apstndb Oct 15, 2024
161e1da
Fix ast_test.go
apstndb Oct 15, 2024
fb67c7a
Update TestDDL
apstndb Oct 15, 2024
5a84774
Improve placement of GenericOptions/GenericOption
apstndb Oct 15, 2024
c3f3a97
Refine Find/FindBool
apstndb Oct 15, 2024
aecd271
Remove GeneratedColumnExpr.IsStored
apstndb Oct 15, 2024
2b459ad
Move SearchIndexOptions methods to correct place
apstndb Oct 15, 2024
57b212a
Fix comment
apstndb Oct 15, 2024
2127e0e
Refactor using Pos arithmetic
apstndb Oct 15, 2024
49f5dcc
Remove unnecessary comment
apstndb Oct 15, 2024
fdefffb
Revert unneeded change
apstndb Oct 15, 2024
b292b49
Merge remote-tracking branch 'origin/main' into feature/search-index-…
apstndb Oct 18, 2024
8671d6b
Add Options
apstndb Oct 18, 2024
a90745c
Migrate to parseCommaSeparatedList
apstndb Oct 18, 2024
4969ac5
Remove p.tryExpect() and p.tryExpectKeywordLike()
apstndb Oct 18, 2024
358d86c
Add *Field methods
apstndb Oct 18, 2024
86f449b
Update testdata
apstndb Oct 18, 2024
6164814
Merge remote-tracking branch 'origin/main' into feature/search-index-…
apstndb Oct 20, 2024
ce4aec8
Merge remote-tracking branch 'origin/main' into feature/search-index-…
apstndb Oct 20, 2024
33a2b05
Merge remote-tracking branch 'origin/main' into feature/search-index-…
apstndb Oct 22, 2024
79842b1
Fix some wording and ordering
apstndb Oct 22, 2024
beb79fb
Add parseIndexAlteration method
apstndb Oct 22, 2024
1e8cfe4
Remove unneeded newlines
apstndb Oct 22, 2024
ca9a1b6
Update ast/ast.go
apstndb Oct 24, 2024
e7f3191
Remove useless use of isnil
apstndb Oct 24, 2024
419e660
Merge remote-tracking branch 'origin/main' into feature/search-index-…
apstndb Oct 24, 2024
ebd86bd
Update testdata
apstndb Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 138 additions & 5 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
package ast

import (
"fmt"
"github.com/cloudspannerecosystem/memefish/token"
)

Expand Down Expand Up @@ -74,6 +75,9 @@ func (Revoke) isStatement() {}
func (CreateChangeStream) isStatement() {}
func (AlterChangeStream) isStatement() {}
func (DropChangeStream) isStatement() {}
func (CreateSearchIndex) isStatement() {}
func (DropSearchIndex) isStatement() {}
func (AlterSearchIndex) isStatement() {}

// QueryExpr represents set operator operands.
type QueryExpr interface {
Expand Down Expand Up @@ -245,6 +249,9 @@ func (Revoke) isDDL() {}
func (CreateChangeStream) isDDL() {}
func (AlterChangeStream) isDDL() {}
func (DropChangeStream) isDDL() {}
func (CreateSearchIndex) isDDL() {}
func (DropSearchIndex) isDDL() {}
func (AlterSearchIndex) isDDL() {}

// Constraint represents table constraint of CONSTARINT clause.
type Constraint interface {
Expand Down Expand Up @@ -934,7 +941,7 @@ type SelectorExpr struct {

// IndexExpr is array item access expression node.
//
// {{.Expr | sql}}[{{if .Ordinal}}ORDINAL{{else}}OFFSET{{end}}({{.Index | sql}})]
// {{.Expr | sql}}[{{if .Ordinal}}ORDINAL{{else}}OFFSET{{end}}({{.Name | sql}})]
type IndexExpr struct {
// pos = Expr.pos
// end = Rbrack + 1
Expand Down Expand Up @@ -1445,6 +1452,59 @@ type CastNumValue struct {
//
// ================================================================================

// GenericOptions is generic OPTIONS clause node without key and value checking.
//
// OPTIONS ({{.Records | sqlJoin ","}})
type GenericOptions struct {
// pos = Options
// end = Rparen + 1

Options token.Pos // position of "OPTIONS" keyword
Rparen token.Pos // position of ")"

Records []*GenericOption // len(Records) > 0
}

// Find finds name in Records, and return its value as Expr.
// The second return value indicates that the name was found in Records.
func (o *GenericOptions) Find(name string) (expr Expr, found bool) {
for _, r := range o.Records {
if r.Name.Name != name {
continue
}
return r.Value, true
}
return nil, false
}

// FindBool finds name in Records, and return its value as *bool.
// If the value is neither bool nor null, it returns error.
func (o *GenericOptions) FindBool(name string) (*bool, error) {
v, ok := o.Find(name)
if !ok {
return nil, nil
}
switch v := v.(type) {
case *BoolLiteral:
return &v.Value, nil
case *NullLiteral:
return nil, nil
default:
return nil, fmt.Errorf("expect bool or null, but have unknown type %T", v)
}
}

// GenericOption is generic option for CREATE statements.
//
// {{.Name | sql}} = {{.Value | sql}}
type GenericOption struct {
// pos = Name.pos
// end = Value.end

Name *Ident
Value Expr
}

// CreateDatabase is CREATE DATABASE statement node.
//
// CREATE DATABASE {{.Name | sql}}
Expand Down Expand Up @@ -1506,10 +1566,11 @@ type CreateSequence struct {
// {{.Type | sql}} {{if .NotNull}}NOT NULL{{end}}
// {{.DefaultExpr | sqlOpt}}
// {{.GeneratedExpr | sqlOpt}}
// {{if not(.Hidden.Invalid())}}HIDDEN{{end}}
// {{.Options | sqlOpt}}
type ColumnDef struct {
// pos = Name.pos
// end = Options.end || GeneratedExpr.end || DefaultExpr.end || Null + 4 || Type.end
// end = Options.end || Hidden + 6 || GeneratedExpr.end || DefaultExpr.end || Null + 4 || Type.end

Null token.Pos // position of "NULL"

Expand All @@ -1518,6 +1579,7 @@ type ColumnDef struct {
NotNull bool
DefaultExpr *ColumnDefaultExpr // optional
GeneratedExpr *GeneratedColumnExpr // optional
Hidden token.Pos // InvalidPos if not hidden
Options *ColumnDefOptions // optional
}

Expand All @@ -1536,13 +1598,14 @@ type ColumnDefaultExpr struct {

// GeneratedColumnExpr is generated column expression.
//
// AS ({{.Expr | sql}}) STORED
// AS ({{.Expr | sql}}) {{if .IsStored}}STORED{{end}}
type GeneratedColumnExpr struct {
// pos = As
// end = Stored
// end = Stored + 6 || Rparen + 1

As token.Pos // position of "AS" keyword
Stored token.Pos // position of "STORED" keyword
Stored token.Pos // position of "STORED" keyword, InvalidPos if not stored
Rparen token.Pos // position of ")"

Expr Expr
}
Expand Down Expand Up @@ -2336,6 +2399,76 @@ type ArraySchemaType struct {
Item SchemaType // ScalarSchemaType or SizedSchemaType
}

// ================================================================================
//
// Search Index DDL
//
// ================================================================================

// CreateSearchIndex represents CREATE SEARCH INDEX statement
//
// CREATE SEARCH INDEX {{.Name | sql}}
// ON {{.TableName | sql}}
// ({{.TokenListPart | sqlJoin ", "}})
// {{.Storing | sqlOpt}}
// {{if not(.PartitionColumns | isnil)}}PARTITION BY {{.PartitionColumns | sqlJoin ", "}}{{end}}
makenowjust marked this conversation as resolved.
Show resolved Hide resolved
// {{.OrderBy | sqlOpt}}
// {{.Where | sqlOpt}}
// {{.Interleave | sqlOpt}}
// {{.Options | sqlOpt}}
type CreateSearchIndex struct {
// pos = Create
// end = (Options ?? Interleave ?? Where ?? OrderBy ?? PartitionColumns[$] ?? Storing).end || Rparen + 1

Create token.Pos

Name *Ident
TableName *Ident
TokenListPart []*Ident
Rparen token.Pos // position of ")" after TokenListPart
Storing *Storing // optional
PartitionColumns []*Ident // optional
OrderBy *OrderBy // optional
Where *Where // optional
Interleave *InterleaveIn // optional
Options *SearchIndexOptions // optional
}

// SearchIndexOptions represents OPTIONS for CREATE SEARCH INDEX statement.
type SearchIndexOptions GenericOptions

func (o *SearchIndexOptions) SortOrderSharding() (*bool, error) {
return (*GenericOptions)(o).FindBool("sort_order_sharding")
}

func (o *SearchIndexOptions) DisableAutomaticUIDColumn() (*bool, error) {
return (*GenericOptions)(o).FindBool("disable_automatic_uid_column")
}

// DropSearchIndex represents DROP SEARCH INDEX statement.
//
// DROP SEARCH INDEX {{.IfExists | strOpt "IF EXISTS "}}{{Name | sql}}
apstndb marked this conversation as resolved.
Show resolved Hide resolved
type DropSearchIndex struct {
// pos = Drop
// end = Name.end

Drop token.Pos
IfExists bool
Name *Ident
}

// AlterSearchIndex represents ALTER SEARCH INDEX statement.
//
// ALTER SEARCH INDEX {{.Name | sql}} {{.IndexAlteration | sql}}
type AlterSearchIndex struct {
// pos = Alter
// end = IndexAlteration.end

Alter token.Pos
Name *Ident
IndexAlteration IndexAlteration
}

// ================================================================================
//
// DML
Expand Down
85 changes: 85 additions & 0 deletions ast/ast_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ast

import (
"github.com/google/go-cmp/cmp"
"testing"
)

Expand Down Expand Up @@ -140,6 +141,9 @@ func TestDDL(t *testing.T) {
DDL(&DropRole{}).isDDL()
DDL(&Grant{}).isDDL()
DDL(&Revoke{}).isDDL()
DDL(&CreateSearchIndex{}).isDDL()
DDL(&DropSearchIndex{}).isDDL()
DDL(&AlterSearchIndex{}).isDDL()
}

func TestConstraint(t *testing.T) {
Expand Down Expand Up @@ -192,3 +196,84 @@ func TestInsertInput(t *testing.T) {
InsertInput(&ValuesInput{}).isInsertInput()
InsertInput(&SubQueryInput{}).isInsertInput()
}

func ptr[T any](v T) *T {
return &v
}

func TestGenericOptionsGetBool(t *testing.T) {
tcases := []struct {
desc string
input *GenericOptions
name string
want *bool
wantErr string
}{
{
desc: "true",
input: &GenericOptions{
Records: []*GenericOption{
{Name: &Ident{Name: "sort_order_sharding"}, Value: &BoolLiteral{Value: true}},
},
},
name: "sort_order_sharding",
want: ptr(true),
},
{
desc: "false",
input: &GenericOptions{
Records: []*GenericOption{
{Name: &Ident{Name: "sort_order_sharding"}, Value: &BoolLiteral{Value: false}},
},
},
name: "sort_order_sharding",
want: ptr(false),
},
{
desc: "explicit null",
input: &GenericOptions{
Records: []*GenericOption{
{Name: &Ident{Name: "sort_order_sharding"}, Value: &NullLiteral{}},
},
},
name: "sort_order_sharding",
want: nil,
},
{
desc: "implicit null",
input: &GenericOptions{
Records: []*GenericOption{
{Name: &Ident{Name: "disable_automatic_uid_column"}, Value: &BoolLiteral{Value: true}},
},
},
name: "sort_order_sharding",
want: nil,
},
{
desc: "invalid value",
input: &GenericOptions{
Records: []*GenericOption{
{Name: &Ident{Name: "sort_order_sharding"}, Value: &StringLiteral{Value: "foo"}},
},
},
name: "sort_order_sharding",
wantErr: "expect bool or null, but have unknown type *ast.StringLiteral",
},
}

for _, tcase := range tcases {
t.Run(tcase.desc, func(t *testing.T) {
got, err := tcase.input.FindBool(tcase.name)
if tcase.wantErr == "" && err != nil {
t.Errorf("should not fail, but: %v", err)
}
if tcase.wantErr != "" && err.Error() != tcase.wantErr {
t.Errorf("should fail, want: %v, got: %v", tcase.wantErr, err)
}
if diff := cmp.Diff(tcase.want, got); diff != "" {
t.Errorf("differ: %v", diff)
}
})
}

}
1 change: 1 addition & 0 deletions ast/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const (
TimestampTypeName ScalarTypeName = "TIMESTAMP"
NumericTypeName ScalarTypeName = "NUMERIC"
JSONTypeName ScalarTypeName = "JSON"
TokenListTypeName ScalarTypeName = "TOKENLIST"
)

type OnDeleteAction string
Expand Down
Loading
Loading