Skip to content

Commit feb4e3d

Browse files
committed
Add JSON_TABLE table function support
Implements parser support for MySQL 8.0.4+ JSON_TABLE, including FOR ORDINALITY, PATH, EXISTS PATH, NESTED [PATH], and DEFAULT/NULL/ ERROR ON EMPTY/ERROR clauses with full restore round-trip. Closes #14
1 parent 845ca69 commit feb4e3d

8 files changed

Lines changed: 12868 additions & 12042 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ y.go
44
.idea/
55
.vscode/
66
coverage.txt
7+
parser/genkeyword

ast/dml.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/sqlc-dev/marino/auth"
2323
"github.com/sqlc-dev/marino/format"
2424
"github.com/sqlc-dev/marino/mysql"
25+
"github.com/sqlc-dev/marino/types"
2526
"github.com/sqlc-dev/marino/util"
2627
)
2728

@@ -53,6 +54,8 @@ var (
5354
_ Node = &TableName{}
5455
_ Node = &TableRefsClause{}
5556
_ Node = &TableSource{}
57+
_ Node = &JSONTable{}
58+
_ Node = &JSONTableColumn{}
5659
_ Node = &SetOprSelectList{}
5760
_ Node = &WildCardField{}
5861
_ Node = &WindowSpec{}
@@ -657,6 +660,203 @@ func (n *TableSource) Accept(v Visitor) (Node, bool) {
657660
return v.Leave(n)
658661
}
659662

663+
// JSONTableColumnTp is the type of a JSON_TABLE column entry.
664+
type JSONTableColumnTp int
665+
666+
const (
667+
// JSONTableColumnForOrdinality is `name FOR ORDINALITY`.
668+
JSONTableColumnForOrdinality JSONTableColumnTp = iota + 1
669+
// JSONTableColumnPath is `name type PATH 'json_path' [on_empty] [on_error]`.
670+
JSONTableColumnPath
671+
// JSONTableColumnExistsPath is `name type EXISTS PATH 'json_path'`.
672+
JSONTableColumnExistsPath
673+
// JSONTableColumnNested is `NESTED [PATH] 'json_path' COLUMNS (column_list)`.
674+
JSONTableColumnNested
675+
)
676+
677+
// JSONTableOnHandlerTp is the behavior choice for ON EMPTY / ON ERROR.
678+
type JSONTableOnHandlerTp int
679+
680+
const (
681+
// JSONTableOnHandlerNull substitutes NULL.
682+
JSONTableOnHandlerNull JSONTableOnHandlerTp = iota + 1
683+
// JSONTableOnHandlerDefault substitutes a literal value.
684+
JSONTableOnHandlerDefault
685+
// JSONTableOnHandlerError raises an error.
686+
JSONTableOnHandlerError
687+
)
688+
689+
// JSONTableOnHandler describes a `{NULL | DEFAULT 'val' | ERROR} ON {EMPTY | ERROR}` clause.
690+
type JSONTableOnHandler struct {
691+
Tp JSONTableOnHandlerTp
692+
DefaultValue string
693+
}
694+
695+
// JSONTableColumn represents one entry in a JSON_TABLE COLUMNS list.
696+
type JSONTableColumn struct {
697+
node
698+
699+
Tp JSONTableColumnTp
700+
Name CIStr
701+
FieldType *types.FieldType
702+
Path string
703+
704+
HasOnEmpty bool
705+
OnEmpty JSONTableOnHandler
706+
HasOnError bool
707+
OnError JSONTableOnHandler
708+
709+
NestedColumns []*JSONTableColumn
710+
}
711+
712+
// Restore implements Node interface.
713+
func (n *JSONTableColumn) Restore(ctx *format.RestoreCtx) error {
714+
switch n.Tp {
715+
case JSONTableColumnForOrdinality:
716+
ctx.WriteName(n.Name.String())
717+
ctx.WriteKeyWord(" FOR ORDINALITY")
718+
case JSONTableColumnPath:
719+
ctx.WriteName(n.Name.String())
720+
ctx.WritePlain(" ")
721+
if err := n.FieldType.Restore(ctx); err != nil {
722+
return annotate(err, "An error occurred while restoring JSONTableColumn.FieldType")
723+
}
724+
ctx.WriteKeyWord(" PATH ")
725+
ctx.WriteString(n.Path)
726+
if n.HasOnEmpty {
727+
if err := n.OnEmpty.restore(ctx, " ON EMPTY"); err != nil {
728+
return err
729+
}
730+
}
731+
if n.HasOnError {
732+
if err := n.OnError.restore(ctx, " ON ERROR"); err != nil {
733+
return err
734+
}
735+
}
736+
case JSONTableColumnExistsPath:
737+
ctx.WriteName(n.Name.String())
738+
ctx.WritePlain(" ")
739+
if err := n.FieldType.Restore(ctx); err != nil {
740+
return annotate(err, "An error occurred while restoring JSONTableColumn.FieldType")
741+
}
742+
ctx.WriteKeyWord(" EXISTS PATH ")
743+
ctx.WriteString(n.Path)
744+
case JSONTableColumnNested:
745+
ctx.WriteKeyWord("NESTED PATH ")
746+
ctx.WriteString(n.Path)
747+
ctx.WriteKeyWord(" COLUMNS")
748+
ctx.WritePlain(" (")
749+
for i, c := range n.NestedColumns {
750+
if i > 0 {
751+
ctx.WritePlain(", ")
752+
}
753+
if err := c.Restore(ctx); err != nil {
754+
return annotatef(err, "An error occurred while restoring JSONTableColumn.NestedColumns[%d]", i)
755+
}
756+
}
757+
ctx.WritePlain(")")
758+
default:
759+
return fmt.Errorf("unknown JSONTableColumnTp %d", n.Tp)
760+
}
761+
return nil
762+
}
763+
764+
// Accept implements Node Accept interface.
765+
func (n *JSONTableColumn) Accept(v Visitor) (Node, bool) {
766+
newNode, skipChildren := v.Enter(n)
767+
if skipChildren {
768+
return v.Leave(newNode)
769+
}
770+
n = newNode.(*JSONTableColumn)
771+
for i, c := range n.NestedColumns {
772+
node, ok := c.Accept(v)
773+
if !ok {
774+
return n, false
775+
}
776+
n.NestedColumns[i] = node.(*JSONTableColumn)
777+
}
778+
return v.Leave(n)
779+
}
780+
781+
func (h *JSONTableOnHandler) restore(ctx *format.RestoreCtx, suffix string) error {
782+
switch h.Tp {
783+
case JSONTableOnHandlerNull:
784+
ctx.WriteKeyWord(" NULL")
785+
case JSONTableOnHandlerError:
786+
ctx.WriteKeyWord(" ERROR")
787+
case JSONTableOnHandlerDefault:
788+
ctx.WriteKeyWord(" DEFAULT ")
789+
ctx.WriteString(h.DefaultValue)
790+
default:
791+
return fmt.Errorf("unknown JSONTableOnHandlerTp %d", h.Tp)
792+
}
793+
ctx.WriteKeyWord(suffix)
794+
return nil
795+
}
796+
797+
// JSONTable represents the MySQL JSON_TABLE table function.
798+
// See https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html
799+
type JSONTable struct {
800+
node
801+
802+
// Expr is the JSON document expression argument.
803+
Expr ExprNode
804+
// Path is the root JSON path string literal.
805+
Path string
806+
// Columns lists the column definitions inside `COLUMNS (...)`.
807+
Columns []*JSONTableColumn
808+
}
809+
810+
func (*JSONTable) resultSet() {}
811+
812+
// Restore implements Node interface.
813+
func (n *JSONTable) Restore(ctx *format.RestoreCtx) error {
814+
ctx.WriteKeyWord("JSON_TABLE")
815+
ctx.WritePlain("(")
816+
if err := n.Expr.Restore(ctx); err != nil {
817+
return annotate(err, "An error occurred while restoring JSONTable.Expr")
818+
}
819+
ctx.WritePlain(", ")
820+
ctx.WriteString(n.Path)
821+
ctx.WriteKeyWord(" COLUMNS")
822+
ctx.WritePlain(" (")
823+
for i, c := range n.Columns {
824+
if i > 0 {
825+
ctx.WritePlain(", ")
826+
}
827+
if err := c.Restore(ctx); err != nil {
828+
return annotatef(err, "An error occurred while restoring JSONTable.Columns[%d]", i)
829+
}
830+
}
831+
ctx.WritePlain(")")
832+
ctx.WritePlain(")")
833+
return nil
834+
}
835+
836+
// Accept implements Node Accept interface.
837+
func (n *JSONTable) Accept(v Visitor) (Node, bool) {
838+
newNode, skipChildren := v.Enter(n)
839+
if skipChildren {
840+
return v.Leave(newNode)
841+
}
842+
n = newNode.(*JSONTable)
843+
if n.Expr != nil {
844+
node, ok := n.Expr.Accept(v)
845+
if !ok {
846+
return n, false
847+
}
848+
n.Expr = node.(ExprNode)
849+
}
850+
for i, c := range n.Columns {
851+
node, ok := c.Accept(v)
852+
if !ok {
853+
return n, false
854+
}
855+
n.Columns[i] = node.(*JSONTableColumn)
856+
}
857+
return v.Leave(n)
858+
}
859+
660860
// SelectLockType is the lock type for SelectStmt.
661861
type SelectLockType int
662862

0 commit comments

Comments
 (0)