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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions oracle/ast/loc.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ func NodeLoc(n Node) Loc {
return v.Loc
case *SubqueryRef:
return v.Loc
case *SubqueryRestrictionClause:
return v.Loc
case *LateralRef:
return v.Loc
case *JoinClause:
Expand Down
88 changes: 60 additions & 28 deletions oracle/ast/outfuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ func writeNode(sb *strings.Builder, node Node) {
writeTableRef(sb, n)
case *SubqueryRef:
writeSubqueryRef(sb, n)
case *SubqueryRestrictionClause:
writeSubqueryRestrictionClause(sb, n)
case *LateralRef:
writeLateralRef(sb, n)
case *XmlTableRef:
Expand Down Expand Up @@ -1123,6 +1125,10 @@ func writeSubqueryRef(sb *strings.Builder, n *SubqueryRef) {
sb.WriteString(" :subquery ")
writeNode(sb, n.Subquery)
}
if n.Restriction != nil {
sb.WriteString(" :restriction ")
writeNode(sb, n.Restriction)
}
if n.Alias != nil {
sb.WriteString(" :alias ")
writeNode(sb, n.Alias)
Expand All @@ -1131,6 +1137,22 @@ func writeSubqueryRef(sb *strings.Builder, n *SubqueryRef) {
sb.WriteString("}")
}

func writeSubqueryRestrictionClause(sb *strings.Builder, n *SubqueryRestrictionClause) {
sb.WriteString("{SUBQUERYRESTRICTION")
if n.ReadOnly {
sb.WriteString(" :readOnly true")
}
if n.CheckOption {
sb.WriteString(" :checkOption true")
}
if n.ConstraintName != nil {
sb.WriteString(" :constraintName ")
writeNode(sb, n.ConstraintName)
}
sb.WriteString(fmt.Sprintf(" :loc_start %d :loc_end %d", n.Loc.Start, n.Loc.End))
sb.WriteString("}")
}

func writeLateralRef(sb *strings.Builder, n *LateralRef) {
sb.WriteString("{LATERAL")
if n.Subquery != nil {
Expand Down Expand Up @@ -2346,20 +2368,25 @@ func writeInsertStmt(sb *strings.Builder, n *InsertStmt) {

func writeUpdateStmt(sb *strings.Builder, n *UpdateStmt) {
sb.WriteString("{UPDATE")
if n.Table != nil {
sb.WriteString(" :table ")
writeNode(sb, n.Table)
}
if n.PartitionExt != nil {
sb.WriteString(" :partitionExt ")
writeNode(sb, n.PartitionExt)
}
if n.Dblink != "" {
sb.WriteString(fmt.Sprintf(" :dblink %q", n.Dblink))
}
if n.Alias != nil {
sb.WriteString(" :alias ")
writeNode(sb, n.Alias)
if n.Target != nil {
sb.WriteString(" :target ")
writeNode(sb, n.Target)
} else {
if n.Table != nil {
sb.WriteString(" :table ")
writeNode(sb, n.Table)
}
if n.PartitionExt != nil {
sb.WriteString(" :partitionExt ")
writeNode(sb, n.PartitionExt)
}
if n.Dblink != "" {
sb.WriteString(fmt.Sprintf(" :dblink %q", n.Dblink))
}
if n.Alias != nil {
sb.WriteString(" :alias ")
writeNode(sb, n.Alias)
}
}
if n.SetClauses != nil {
sb.WriteString(" :setClauses ")
Expand Down Expand Up @@ -2391,20 +2418,25 @@ func writeUpdateStmt(sb *strings.Builder, n *UpdateStmt) {

func writeDeleteStmt(sb *strings.Builder, n *DeleteStmt) {
sb.WriteString("{DELETE")
if n.Table != nil {
sb.WriteString(" :table ")
writeNode(sb, n.Table)
}
if n.PartitionExt != nil {
sb.WriteString(" :partitionExt ")
writeNode(sb, n.PartitionExt)
}
if n.Dblink != "" {
sb.WriteString(fmt.Sprintf(" :dblink %q", n.Dblink))
}
if n.Alias != nil {
sb.WriteString(" :alias ")
writeNode(sb, n.Alias)
if n.Target != nil {
sb.WriteString(" :target ")
writeNode(sb, n.Target)
} else {
if n.Table != nil {
sb.WriteString(" :table ")
writeNode(sb, n.Table)
}
if n.PartitionExt != nil {
sb.WriteString(" :partitionExt ")
writeNode(sb, n.PartitionExt)
}
if n.Dblink != "" {
sb.WriteString(fmt.Sprintf(" :dblink %q", n.Dblink))
}
if n.Alias != nil {
sb.WriteString(" :alias ")
writeNode(sb, n.Alias)
}
}
if n.WhereClause != nil {
sb.WriteString(" :whereClause ")
Expand Down
21 changes: 18 additions & 3 deletions oracle/ast/parsenodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -749,14 +749,27 @@ func (n *TableRef) tableExpr() {}

// SubqueryRef represents a subquery in a FROM clause.
type SubqueryRef struct {
Subquery StmtNode // the subquery
Alias *Alias // alias
Loc Loc
Subquery StmtNode // the subquery
Restriction *SubqueryRestrictionClause // WITH READ ONLY / WITH CHECK OPTION
Alias *Alias // alias
Loc Loc
}

func (n *SubqueryRef) nodeTag() {}
func (n *SubqueryRef) tableExpr() {}

// SubqueryRestrictionClause represents a DML inline-view subquery restriction.
//
// WITH { READ ONLY | CHECK OPTION [ CONSTRAINT constraint_name ] }
type SubqueryRestrictionClause struct {
ReadOnly bool // WITH READ ONLY
CheckOption bool // WITH CHECK OPTION
ConstraintName *ObjectName // optional CONSTRAINT name
Loc Loc
}

func (n *SubqueryRestrictionClause) nodeTag() {}

// LateralRef represents a LATERAL inline view in FROM.
//
// LATERAL ( subquery ) [ alias ]
Expand Down Expand Up @@ -1339,6 +1352,7 @@ func (n *ErrorLogClause) nodeTag() {}
// UpdateStmt represents an UPDATE statement.
// Ref: https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/UPDATE.html
type UpdateStmt struct {
Target TableExpr // table, inline view, or table collection target
Table *ObjectName // table to update
PartitionExt *PartitionExtClause // PARTITION/SUBPARTITION extension
Dblink string // @dblink name
Expand Down Expand Up @@ -1372,6 +1386,7 @@ func (n *SetClause) nodeTag() {}
// DeleteStmt represents a DELETE statement.
// Ref: https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/DELETE.html
type DeleteStmt struct {
Target TableExpr // table, inline view, or table collection target
Table *ObjectName // table to delete from
PartitionExt *PartitionExtClause // PARTITION/SUBPARTITION extension
Dblink string // @dblink name
Expand Down
47 changes: 31 additions & 16 deletions oracle/ast/walk_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion oracle/parser/create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ func isOracleTypeToken(tokenType int) bool {
switch tokenType {
case kwNUMBER, kwINTEGER, kwSMALLINT, kwDECIMAL, kwFLOAT,
kwCHAR, kwVARCHAR2, kwVARCHAR, kwNCHAR, kwNVARCHAR2,
kwCLOB, kwBLOB, kwNCLOB,
kwCLOB, kwBLOB, kwNCLOB, kwJSON,
kwDATE, kwTIMESTAMP, kwINTERVAL,
kwRAW, kwLONG, kwROWID:
return true
Expand Down
29 changes: 29 additions & 0 deletions oracle/parser/create_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,35 @@ func TestP2CreateTableMalformedComplexOption(t *testing.T) {
ParseShouldFail(t, "CREATE TABLE docs (id NUMBER, doc CLOB) LOB STORE AS lob_seg")
}

func TestCreateTableJsonColumnType(t *testing.T) {
stmt := parseCreateTableForP2(t, "CREATE TABLE docs (id NUMBER, payload JSON)")
if stmt.Columns == nil || stmt.Columns.Len() != 2 {
t.Fatalf("expected 2 columns, got %#v", stmt.Columns)
}
col := stmt.Columns.Items[1].(*ast.ColumnDef)
if col.TypeName == nil || col.TypeName.Names.Len() != 1 {
t.Fatalf("expected JSON type name, got %#v", col.TypeName)
}
if got := col.TypeName.Names.Items[0].(*ast.String).Str; got != "JSON" {
t.Fatalf("expected JSON type name, got %q", got)
}
}

func TestCreateTableJsonKeywordStillAllowedAsIdentifier(t *testing.T) {
parseCreateTableForP2(t, "CREATE TABLE JSON (a NUMBER)")
stmt := parseCreateTableForP2(t, "CREATE TABLE t (JSON NUMBER)")
if stmt.Columns == nil || stmt.Columns.Len() != 1 {
t.Fatalf("expected 1 column, got %#v", stmt.Columns)
}
col := stmt.Columns.Items[0].(*ast.ColumnDef)
if col.Name != "JSON" {
t.Fatalf("expected column name JSON, got %q", col.Name)
}
if col.TypeName == nil || col.TypeName.Names.Items[0].(*ast.String).Str != "NUMBER" {
t.Fatalf("expected NUMBER type, got %#v", col.TypeName)
}
}

func TestCreateTableIntervalPartitioning(t *testing.T) {
ParseAndCheck(t, `CREATE TABLE t_interval_full (d DATE)
PARTITION BY RANGE (d)
Expand Down
1 change: 1 addition & 0 deletions oracle/parser/testdata/coverage/loc_node_coverage.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ node_type family status fixture notes debt_class approval next_action
*nodes.Hint ast covered SELECT /*+ INDEX(t idx) */ * FROM t direct parser fixture added by strict Loc audit none covered keep_regression_guard
*nodes.TableRef clause covered SELECT * FROM t table ref span none covered keep_regression_guard
*nodes.SubqueryRef ast covered SELECT * FROM (SELECT 1 a FROM dual) q direct parser fixture added by strict Loc audit none covered keep_regression_guard
*nodes.SubqueryRestrictionClause clause covered UPDATE (SELECT a FROM t WITH CHECK OPTION CONSTRAINT ck_v) v SET a = 1 DML inline-view subquery restriction span none covered keep_regression_guard
*nodes.LateralRef ast covered SELECT * FROM t, LATERAL (SELECT 1 a FROM dual) q direct parser fixture added by strict Loc audit none covered keep_regression_guard
*nodes.JoinClause clause covered SELECT * FROM t JOIN u ON t.id = u.id join clause span none covered keep_regression_guard
*nodes.WithClause clause covered WITH q AS (SELECT 1 a FROM dual) SELECT a FROM q direct parser fixture added by strict Loc audit none covered keep_regression_guard
Expand Down
2 changes: 1 addition & 1 deletion oracle/parser/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (p *Parser) parseTypeName() (*nodes.TypeName, error) {
return nil, parseErr1117
}

case kwCLOB, kwBLOB, kwNCLOB:
case kwCLOB, kwBLOB, kwNCLOB, kwJSON:
tn.Names.Items = append(tn.Names.Items, &nodes.String{Str: p.cur.Str})
p.advance()

Expand Down
2 changes: 1 addition & 1 deletion oracle/parser/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func TestParseTypeChar(t *testing.T) {

// TestParseTypeLOB tests CLOB, BLOB, NCLOB.
func TestParseTypeLOB(t *testing.T) {
for _, input := range []string{"CLOB", "BLOB", "NCLOB"} {
for _, input := range []string{"CLOB", "BLOB", "NCLOB", "JSON"} {
t.Run(input, func(t *testing.T) {
p := newTestParser(input)
tn, parseErr3 := p.parseTypeName()
Expand Down
Loading
Loading