Files
tidb/field/result_field.go
qiuyesuifeng 4aeaeada1d *: clean up
2015-10-20 12:19:06 +08:00

204 lines
5.4 KiB
Go

// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.
// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.
package field
import (
"fmt"
"strings"
"github.com/juju/errors"
"github.com/pingcap/tidb/column"
"github.com/pingcap/tidb/mysql"
)
// ResultField provides meta data of table column.
type ResultField struct {
column.Col // Col.Name is OrgName.
Name string
TableName string
OrgTableName string
DBName string
}
// String implements fmt.Stringer interface.
func (rf *ResultField) String() string {
return JoinQualifiedName(rf.DBName, rf.TableName, rf.Name)
}
// Clone clones a new ResultField from old ResultField.
func (rf *ResultField) Clone() *ResultField {
r := *rf
return &r
}
// RFQNames gets all ResultField names.
func RFQNames(l []*ResultField) []string {
r := make([]string, len(l))
for i, v := range l {
r[i] = fmt.Sprintf("%q", v.Name)
}
return r
}
// ColsToResultFields converts Cols to ResultFields.
func ColsToResultFields(cols []*column.Col, tableName string) []*ResultField {
// TODO: add DBName
r := make([]*ResultField, len(cols))
for i, v := range cols {
r[i] = ColToResultField(v, tableName)
}
return r
}
// ColToResultField converts Col to ResultField.
func ColToResultField(col *column.Col, tableName string) *ResultField {
// TODO: add DBName.
rf := &ResultField{
Col: *col,
Name: col.Name.O,
TableName: tableName,
OrgTableName: tableName,
}
// Keep things compatible for old clients.
// Refer to mysql-server/sql/protocol.cc send_result_set_metadata()
if rf.Tp == mysql.TypeVarchar {
rf.Tp = mysql.TypeVarString
}
return rf
}
// ContainFieldName checks whether name is in ResultFields.
func ContainFieldName(name string, fields []*ResultField) bool {
indices := GetResultFieldIndex(name, fields)
return len(indices) > 0
}
// ContainAllFieldNames checks whether names are all in ResultFields.
// TODO: add alias table name support
func ContainAllFieldNames(names []string, fields []*ResultField) bool {
for _, name := range names {
if !ContainFieldName(name, fields) {
return false
}
}
return true
}
// SplitQualifiedName splits an identifier name to db, table and field name.
func SplitQualifiedName(name string) (db string, table string, field string) {
seps := strings.Split(name, ".")
l := len(seps)
switch l {
case 1:
// `name` is field.
field = seps[0]
case 2:
// `name` is `table.field`.
table, field = seps[0], seps[1]
case 3:
// `name` is `db.table.field`.
db, table, field = seps[0], seps[1], seps[2]
default:
// `name` is `db.table.field`.
db, table, field = seps[l-3], seps[l-2], seps[l-1]
}
return
}
// JoinQualifiedName converts db, table, field to a qualified name.
func JoinQualifiedName(db string, table string, field string) string {
if len(db) > 0 {
return fmt.Sprintf("%s.%s.%s", db, table, field)
} else if len(table) > 0 {
return fmt.Sprintf("%s.%s", table, field)
} else {
return field
}
}
// GetResultFieldIndex gets name index in ResultFields.
func GetResultFieldIndex(name string, fields []*ResultField) []int {
var indices []int
db, table, field := SplitQualifiedName(name)
for i, f := range fields {
if checkFieldsEqual(db, table, field, f.DBName, f.TableName, f.Name) {
indices = append(indices, i)
continue
}
}
return indices
}
// CheckFieldsEqual checks if xname and yname is equal.
// xname/yname in the pattern dbname.tablename.fieldname
// If any part of any argument is missing, it will ignore it and
// continue check other parts. So a.b.c equals b.c
func CheckFieldsEqual(xname, yname string) bool {
xdb, xtable, xfield := SplitQualifiedName(xname)
ydb, ytable, yfield := SplitQualifiedName(yname)
return checkFieldsEqual(xdb, xtable, xfield, ydb, ytable, yfield)
}
// See: https://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html
// TODO: check `lower_case_table_names` system variable.
func checkFieldsEqual(xdb, xtable, xfield, ydb, ytable, yfield string) bool {
if !strings.EqualFold(xfield, yfield) {
return false
}
if len(xtable) > 0 && len(ytable) > 0 {
if !strings.EqualFold(xtable, ytable) {
return false
}
}
if len(xdb) > 0 && len(ydb) > 0 {
if !strings.EqualFold(xdb, ydb) {
return false
}
}
return true
}
// CloneFieldByName clones a ResultField in ResultFields according to name.
func CloneFieldByName(name string, fields []*ResultField) (*ResultField, error) {
indices := GetResultFieldIndex(name, fields)
if len(indices) == 0 {
return nil, errors.Errorf("unknown field %s", name)
}
return fields[indices[0]].Clone(), nil
}
// CheckWildcardField checks wildcard field, like `*` or `table.*` or `db.table.*`.
func CheckWildcardField(name string) (string, bool, error) {
_, table, field := SplitQualifiedName(name)
return table, field == "*", nil
}
// IsQualifiedName returns whether name contains "." or not.
func IsQualifiedName(name string) bool {
return strings.Contains(name, ".")
}