Skip to content

Commit

Permalink
Rework QualifiedName.
Browse files Browse the repository at this point in the history
QualifiedName is now composed of a base name and an indirection
expression. Elements in an indirection expression are either ".<name>",
".*" or "[<begin>:<end>]".

Fixes #1810.
  • Loading branch information
petermattis committed Aug 3, 2015
1 parent f25413d commit b7b792b
Show file tree
Hide file tree
Showing 21 changed files with 5,993 additions and 5,924 deletions.
4 changes: 1 addition & 3 deletions sql/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ func (p *planner) CreateDatabase(n *parser.CreateDatabase) (planNode, error) {

// CreateTable creates a table.
func (p *planner) CreateTable(n *parser.CreateTable) (planNode, error) {
var err error
n.Table, err = p.normalizeTableName(n.Table)
if err != nil {
if err := p.normalizeTableName(n.Table); err != nil {
return nil, err
}

Expand Down
2 changes: 1 addition & 1 deletion sql/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (p *planner) Delete(n *parser.Delete) (planNode, error) {
// deleting.
node, err := p.Select(&parser.Select{
Exprs: parser.SelectExprs{
&parser.StarExpr{TableName: parser.QualifiedName{tableDesc.Name}},
&parser.StarExpr{TableName: &parser.QualifiedName{Base: parser.Name(tableDesc.Name)}},
},
From: parser.TableExprs{n.Table},
Where: n.Where,
Expand Down
2 changes: 1 addition & 1 deletion sql/parser/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (node *IndexTableDef) String() string {
// CreateTable represents a CREATE TABLE statement.
type CreateTable struct {
IfNotExists bool
Table QualifiedName
Table *QualifiedName
Defs TableDefs
}

Expand Down
5 changes: 5 additions & 0 deletions sql/parser/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ package parser
import (
"bytes"
"fmt"
"strings"
)

var (
Expand Down Expand Up @@ -60,6 +61,10 @@ func encodeSQLString(buf []byte, in []byte) []byte {

// TODO(pmattis): This method needs testing.
func encodeSQLIdent(buf *bytes.Buffer, s string) {
if _, ok := keywords[strings.ToUpper(s)]; ok {
fmt.Fprintf(buf, "\"%s\"", s)
return
}
// The string needs quoting if it does not match the ident format.
if isIdent(s) {
_, _ = buf.WriteString(s)
Expand Down
4 changes: 2 additions & 2 deletions sql/parser/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ func EvalExpr(expr Expr, env Env) (Datum, error) {

switch t := expr.(type) {
case Row:
// Row and Tuple are synonmous: convert Row to Tuple to simplify logic
// Row and Tuple are synonymous: convert Row to Tuple to simplify logic
// below.
expr = Tuple(t)
}
Expand Down Expand Up @@ -415,7 +415,7 @@ func EvalExpr(expr Expr, env Env) (Datum, error) {
case NullVal:
return null, nil

case QualifiedName:
case *QualifiedName:
if d, ok := env.Get(t.String()); ok {
return d, nil
}
Expand Down
59 changes: 32 additions & 27 deletions sql/parser/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"bytes"
"fmt"
"strconv"
"strings"
)

// Expr represents an expression.
Expand All @@ -44,7 +43,7 @@ func (NumVal) expr() {}
func (BoolVal) expr() {}
func (ValArg) expr() {}
func (NullVal) expr() {}
func (QualifiedName) expr() {}
func (*QualifiedName) expr() {}
func (Tuple) expr() {}
func (Row) expr() {}
func (*Subquery) expr() {}
Expand All @@ -53,6 +52,7 @@ func (*UnaryExpr) expr() {}
func (*FuncExpr) expr() {}
func (*CaseExpr) expr() {}
func (*CastExpr) expr() {}
func (*StarExpr) expr() {}
func (DBool) expr() {}
func (DInt) expr() {}
func (DFloat) expr() {}
Expand Down Expand Up @@ -241,20 +241,38 @@ func (NullVal) String() string {
return fmt.Sprintf("NULL")
}

// QualifiedName is a dot separated list of names.
type QualifiedName []string
// QualifiedName is a base name and an optional indirection expression.
type QualifiedName struct {
Base Name
Indirect Indirection
}

// Database returns the database portion of the name.
func (n QualifiedName) Database() string {
if len(n) > 1 {
return n[0]
func (n *QualifiedName) Database() string {
// The database portion of the name is n.Base, but only as long as an
// indirection is present. In a qualified name like "foo" (without an
// indirection), "foo" represents a table or column name.
if len(n.Indirect) > 0 {
return n.Base.String()
}
return ""
}

// Table returns the table portion of the name.
func (n QualifiedName) Table() string {
return n[len(n)-1]
//
// TOOD(pmattis): See the comment for Column() regarding how QualifiedNames are
// used in context sensitive locations. Sometimes they are referring to tables
// sometimes to columns.
func (n *QualifiedName) Table() string {
if l := len(n.Indirect); l > 0 {
last := n.Indirect[l-1]
switch t := last.(type) {
case NameIndirection:
return Name(t).String()
}
return ""
}
return n.Base.String()
}

// Column returns the column portion of the name.
Expand All @@ -266,30 +284,17 @@ func (n QualifiedName) Table() string {
// determining whether it is a table or column name. Perhaps we can have
// different types for use in the different contexts (e.g. ColumnName,
// TableName, IndexName, etc).
func (n QualifiedName) Column() string {
func (n *QualifiedName) Column() string {
return n.Table()
}

func (n QualifiedName) String() string {
var buf bytes.Buffer
for i, s := range n {
if i > 0 {
_, _ = buf.WriteString(".")
}
if s == "*" {
_, _ = buf.WriteString(s)
} else if _, ok := keywords[strings.ToUpper(s)]; ok {
fmt.Fprintf(&buf, "\"%s\"", s)
} else {
encodeSQLIdent(&buf, s)
}
}
return buf.String()
func (n *QualifiedName) String() string {
return fmt.Sprintf("%s%s", n.Base, n.Indirect)
}

// QualifiedNames represents a command separated list (see the String method)
// of qualified names.
type QualifiedNames []QualifiedName
type QualifiedNames []*QualifiedName

func (n QualifiedNames) String() string {
var buf bytes.Buffer
Expand Down Expand Up @@ -422,7 +427,7 @@ func (node *UnaryExpr) String() string {

// FuncExpr represents a function call.
type FuncExpr struct {
Name QualifiedName
Name *QualifiedName
Distinct bool
Exprs Exprs
}
Expand Down
4 changes: 2 additions & 2 deletions sql/parser/expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestQualifiedNameString(t *testing.T) {
testCases := []struct {
in, out string
}{
{"*", "*"},
{"*", `"*"`},
// Keyword.
{"DATABASE", `"DATABASE"`},
{"dAtAbAse", `"dAtAbAse"`},
Expand All @@ -39,7 +39,7 @@ func TestQualifiedNameString(t *testing.T) {
}

for _, tc := range testCases {
q := QualifiedName{tc.in}
q := &QualifiedName{Base: Name(tc.in)}
if q.String() != tc.out {
t.Errorf("expected q.String() == %q, got %q", tc.out, q.String())
}
Expand Down
77 changes: 77 additions & 0 deletions sql/parser/indirection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis ([email protected])

package parser

import (
"bytes"
"fmt"
)

// IndirectionElem is a single element in an indirection expression.
type IndirectionElem interface {
indirectionElem()
String() string
}

func (NameIndirection) indirectionElem() {}
func (StarIndirection) indirectionElem() {}
func (*ArrayIndirection) indirectionElem() {}

// Indirection represents an indirection expression composed of a series of
// indirection elements.
type Indirection []IndirectionElem

func (i Indirection) String() string {
var buf bytes.Buffer
for _, e := range i {
_, _ = buf.WriteString(e.String())
}
return buf.String()
}

// NameIndirection represents ".<name>" in an indirection expression.
type NameIndirection Name

func (n NameIndirection) String() string {
return fmt.Sprintf(".%s", Name(n))
}

// StarIndirection represents ".*" in an indirection expression.
type StarIndirection struct{}

func (StarIndirection) String() string {
return ".*"
}

// ArrayIndirection represents "[<begin>:<end>]" in an indirection expression.
type ArrayIndirection struct {
Begin Expr
End Expr
}

func (a *ArrayIndirection) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("[")
_, _ = buf.WriteString(a.Begin.String())
if a.End != nil {
_, _ = buf.WriteString(":")
_, _ = buf.WriteString(a.End.String())
}
_, _ = buf.WriteString("]")
return buf.String()
}
2 changes: 1 addition & 1 deletion sql/parser/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

// Insert represents an INSERT statement.
type Insert struct {
Table QualifiedName
Table *QualifiedName
Columns QualifiedNames
Rows SelectStatement
}
Expand Down
4 changes: 4 additions & 0 deletions sql/parser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func TestParse(t *testing.T) {
{`SELECT 1 FROM t`},
{`SELECT 1, 2 FROM t`},
{`SELECT * FROM t`},
{`SELECT "*" FROM t`},
{`SELECT a, b FROM t`},
{`SELECT a AS b FROM t`},
{`SELECT a.* FROM t`},
Expand All @@ -122,6 +123,9 @@ func TestParse(t *testing.T) {
{`SELECT 0.1 FROM t`},
{`SELECT a FROM t`},
{`SELECT a.b FROM t`},
{`SELECT a.b.* FROM t`},
{`SELECT a.b[1] FROM t`},
{`SELECT a.b[1 + 1:4][3] FROM t`},
{`SELECT 'a' FROM t`},

{`SELECT 'a' AS "12345"`},
Expand Down
11 changes: 4 additions & 7 deletions sql/parser/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,14 @@ func (*NonStarExpr) selectExpr() {}
// TODO(tschottdorf): needs work, see #1810. TableName is never even referenced
// in the grammar so far.
type StarExpr struct {
Expr
TableName QualifiedName
TableName *QualifiedName
}

func (node *StarExpr) String() string {
var buf bytes.Buffer
if len(node.TableName) != 0 {
fmt.Fprintf(&buf, "%s.", node.TableName)
if node.TableName != nil {
return fmt.Sprintf("%s.*", node.TableName)
}
fmt.Fprintf(&buf, "*")
return buf.String()
return "*"
}

// NonStarExpr defines a non-'*' select expr.
Expand Down
2 changes: 1 addition & 1 deletion sql/parser/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import "fmt"

// Set represents a SET statement.
type Set struct {
Name QualifiedName
Name *QualifiedName
Values Exprs
}

Expand Down
6 changes: 3 additions & 3 deletions sql/parser/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

// ShowColumns represents a SHOW COLUMNS statement.
type ShowColumns struct {
Table QualifiedName
Table *QualifiedName
}

func (node *ShowColumns) String() string {
Expand All @@ -50,7 +50,7 @@ func (node *ShowDatabases) String() string {

// ShowIndex represents a SHOW INDEX statement.
type ShowIndex struct {
Table QualifiedName
Table *QualifiedName
}

func (node *ShowIndex) String() string {
Expand All @@ -59,7 +59,7 @@ func (node *ShowIndex) String() string {

// ShowTables represents a SHOW TABLES statement.
type ShowTables struct {
Name QualifiedName
Name *QualifiedName
}

func (node *ShowTables) String() string {
Expand Down
Loading

0 comments on commit b7b792b

Please sign in to comment.