2019-03-02 12:34:08 +01:00
|
|
|
// Modeling of columns
|
|
|
|
|
|
|
|
|
|
package sqlbuilder
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"regexp"
|
2019-03-30 10:17:32 +01:00
|
|
|
"strings"
|
2019-03-02 12:34:08 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// XXX: Maybe add UIntColumn
|
|
|
|
|
|
2019-03-30 10:17:32 +01:00
|
|
|
// Representation of a tableName for query generation
|
2019-03-02 12:34:08 +01:00
|
|
|
type Column interface {
|
2019-03-31 09:17:28 +02:00
|
|
|
Expression
|
2019-03-17 20:41:03 +01:00
|
|
|
|
2019-03-02 12:34:08 +01:00
|
|
|
Name() string
|
2019-03-30 10:17:32 +01:00
|
|
|
TableName() string
|
|
|
|
|
// Internal function for tracking tableName that a column belongs to
|
2019-03-02 12:34:08 +01:00
|
|
|
// for the purpose of serialization
|
|
|
|
|
setTableName(table string) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type NullableColumn bool
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
Nullable NullableColumn = true
|
|
|
|
|
NotNullable NullableColumn = false
|
|
|
|
|
)
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
//// A column that can be refer to outside of the projection list
|
|
|
|
|
//type NonAliasColumn interface {
|
|
|
|
|
// Column
|
|
|
|
|
//}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
|
|
|
|
type Collation string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
UTF8CaseInsensitive Collation = "utf8_unicode_ci"
|
|
|
|
|
UTF8CaseSensitive Collation = "utf8_unicode"
|
|
|
|
|
UTF8Binary Collation = "utf8_bin"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Representation of MySQL charsets
|
|
|
|
|
type Charset string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
UTF8 Charset = "utf8"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// The base type for real materialized columns.
|
|
|
|
|
type baseColumn struct {
|
2019-03-31 09:17:28 +02:00
|
|
|
expressionInterfaceImpl
|
2019-03-31 14:07:58 +02:00
|
|
|
|
2019-03-30 10:17:32 +01:00
|
|
|
name string
|
|
|
|
|
nullable NullableColumn
|
|
|
|
|
tableName string
|
2019-03-17 20:41:03 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
func newBaseColumn(name string, nullable NullableColumn, tableName string, parent Column) baseColumn {
|
|
|
|
|
bc := baseColumn{
|
|
|
|
|
name: name,
|
|
|
|
|
nullable: nullable,
|
|
|
|
|
tableName: tableName,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bc.expressionInterfaceImpl.parent = parent
|
|
|
|
|
|
|
|
|
|
return bc
|
|
|
|
|
}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
|
|
|
|
func (c *baseColumn) Name() string {
|
|
|
|
|
return c.name
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-30 10:17:32 +01:00
|
|
|
func (c *baseColumn) TableName() string {
|
|
|
|
|
return c.tableName
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-02 12:34:08 +01:00
|
|
|
func (c *baseColumn) setTableName(table string) error {
|
2019-03-30 10:17:32 +01:00
|
|
|
c.tableName = table
|
2019-03-02 12:34:08 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
func (c baseColumn) SerializeSql(out *bytes.Buffer, options ...serializeOption) error {
|
2019-03-30 10:17:32 +01:00
|
|
|
if c.tableName != "" {
|
|
|
|
|
_, _ = out.WriteString(c.tableName)
|
2019-03-09 14:20:44 +01:00
|
|
|
_, _ = out.WriteString(".")
|
|
|
|
|
}
|
2019-03-30 10:17:32 +01:00
|
|
|
containsDot := strings.Contains(c.name, ".")
|
|
|
|
|
|
|
|
|
|
if containsDot {
|
|
|
|
|
out.WriteString("\"")
|
|
|
|
|
}
|
2019-03-09 14:20:44 +01:00
|
|
|
_, _ = out.WriteString(c.name)
|
2019-03-30 10:17:32 +01:00
|
|
|
if containsDot {
|
|
|
|
|
out.WriteString("\"")
|
|
|
|
|
}
|
2019-03-09 14:20:44 +01:00
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
if contains(options, FOR_PROJECTION) && !contains(options, ALIASED) && c.tableName != "" {
|
|
|
|
|
_, _ = out.WriteString(" AS \"" + c.tableName + "." + c.name + "\"")
|
2019-03-02 12:34:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
return nil
|
2019-03-02 12:34:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
//
|
|
|
|
|
//type bytesColumn struct {
|
|
|
|
|
// baseColumn
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//// Representation of VARBINARY/BLOB columns
|
|
|
|
|
//// This function will panic if name is not valid
|
|
|
|
|
//func BytesColumn(name string, nullable NullableColumn) Column {
|
|
|
|
|
// if !validIdentifierName(name) {
|
|
|
|
|
// panic("Invalid column name in bytes column")
|
|
|
|
|
// }
|
|
|
|
|
// bc := &bytesColumn{}
|
|
|
|
|
// bc.name = name
|
|
|
|
|
// bc.nullable = nullable
|
|
|
|
|
// return bc
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//type stringColumn struct {
|
|
|
|
|
// baseColumn
|
|
|
|
|
// charset Charset
|
|
|
|
|
// collation Collation
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//// Representation of VARCHAR/TEXT columns
|
|
|
|
|
//// This function will panic if name is not valid
|
|
|
|
|
//func StrColumn(
|
|
|
|
|
// name string,
|
|
|
|
|
// charset Charset,
|
|
|
|
|
// collation Collation,
|
|
|
|
|
// nullable NullableColumn) Column {
|
|
|
|
|
//
|
|
|
|
|
// if !validIdentifierName(name) {
|
|
|
|
|
// panic("Invalid column name in str column")
|
|
|
|
|
// }
|
|
|
|
|
// sc := &stringColumn{charset: charset, collation: collation}
|
|
|
|
|
// sc.name = name
|
|
|
|
|
// sc.nullable = nullable
|
|
|
|
|
// return sc
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//type dateTimeColumn struct {
|
|
|
|
|
// baseColumn
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//// Representation of DateTime columns
|
|
|
|
|
//// This function will panic if name is not valid
|
|
|
|
|
//func DateTimeColumn(name string, nullable NullableColumn) Column {
|
|
|
|
|
// if !validIdentifierName(name) {
|
|
|
|
|
// panic("Invalid column name in datetime column")
|
|
|
|
|
// }
|
|
|
|
|
// dc := &dateTimeColumn{}
|
|
|
|
|
// dc.name = name
|
|
|
|
|
// dc.nullable = nullable
|
|
|
|
|
// return dc
|
|
|
|
|
//}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
//type IntegerColumn struct {
|
|
|
|
|
// baseColumn
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//// Representation of any integer column
|
|
|
|
|
//// This function will panic if name is not valid
|
|
|
|
|
//func IntColumn(name string, nullable NullableColumn) *IntegerColumn {
|
|
|
|
|
// if !validIdentifierName(name) {
|
|
|
|
|
// panic("Invalid column name in int column")
|
|
|
|
|
// }
|
|
|
|
|
// ic := &IntegerColumn{}
|
|
|
|
|
// ic.name = name
|
|
|
|
|
// ic.nullable = nullable
|
|
|
|
|
// return ic
|
|
|
|
|
//}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
//type doubleColumn struct {
|
|
|
|
|
// baseColumn
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//// Representation of any double column
|
|
|
|
|
//// This function will panic if name is not valid
|
|
|
|
|
//func DoubleColumn(name string, nullable NullableColumn) Column {
|
|
|
|
|
// if !validIdentifierName(name) {
|
|
|
|
|
// panic("Invalid column name in int column")
|
|
|
|
|
// }
|
|
|
|
|
// ic := &doubleColumn{}
|
|
|
|
|
// ic.name = name
|
|
|
|
|
// ic.nullable = nullable
|
|
|
|
|
// return ic
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//type booleanColumn struct {
|
|
|
|
|
// baseColumn
|
|
|
|
|
//
|
|
|
|
|
// // XXX: Maybe allow isBoolExpression (for now, not included because
|
|
|
|
|
// // the deferred lookup equivalent can never be isBoolExpression)
|
|
|
|
|
//}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
|
|
|
|
// Representation of TINYINT used as a bool
|
|
|
|
|
// This function will panic if name is not valid
|
2019-03-31 14:07:58 +02:00
|
|
|
//func NewBoolColumn(name string, nullable NullableColumn) Column {
|
|
|
|
|
// if !validIdentifierName(name) {
|
|
|
|
|
// panic("Invalid column name in bool column")
|
|
|
|
|
// }
|
|
|
|
|
// bc := &booleanColumn{}
|
|
|
|
|
// bc.name = name
|
|
|
|
|
// bc.nullable = nullable
|
|
|
|
|
// return bc
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//type aliasColumn struct {
|
|
|
|
|
// baseColumn
|
|
|
|
|
// expression Expression
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *aliasColumn) SerializeSql(out *bytes.Buffer) error {
|
|
|
|
|
// _ = out.WriteByte('`')
|
|
|
|
|
// _, _ = out.WriteString(c.name)
|
|
|
|
|
// _ = out.WriteByte('`')
|
|
|
|
|
// return nil
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *aliasColumn) SerializeSqlForColumnList(out *bytes.Buffer) error {
|
|
|
|
|
// if !validIdentifierName(c.name) {
|
|
|
|
|
// return errors.Newf(
|
|
|
|
|
// "Invalid alias name `%s`. Generated sql: %s",
|
|
|
|
|
// c.name,
|
|
|
|
|
// out.String())
|
|
|
|
|
// }
|
|
|
|
|
// if c.expression == nil {
|
|
|
|
|
// return errors.Newf(
|
|
|
|
|
// "Cannot alias a nil expression. Generated sql: %s",
|
|
|
|
|
// out.String())
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// _ = out.WriteByte('(')
|
|
|
|
|
// if c.expression == nil {
|
|
|
|
|
// return errors.Newf("nil alias clause. Generate sql: %s", out.String())
|
|
|
|
|
// }
|
|
|
|
|
// if err := c.expression.SerializeSql(out); err != nil {
|
|
|
|
|
// return err
|
|
|
|
|
// }
|
|
|
|
|
// _, _ = out.WriteString(") AS \"")
|
|
|
|
|
// _, _ = out.WriteString(c.name)
|
|
|
|
|
// _ = out.WriteByte('"')
|
|
|
|
|
// return nil
|
|
|
|
|
//}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
2019-03-31 14:07:58 +02:00
|
|
|
//func (c *aliasColumn) setTableName(table string) error {
|
|
|
|
|
// return errors.Newf(
|
|
|
|
|
// "Alias column '%s' should never have setTableName called on it",
|
|
|
|
|
// c.name)
|
|
|
|
|
//}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
|
|
|
|
// Representation of aliased clauses (expression AS name)
|
2019-03-31 09:17:28 +02:00
|
|
|
//func Alias(name string, c Expression) Column {
|
|
|
|
|
// ac := &aliasColumn{}
|
|
|
|
|
// ac.name = name
|
|
|
|
|
// ac.expression = c
|
|
|
|
|
// return ac
|
|
|
|
|
//}
|
2019-03-02 12:34:08 +01:00
|
|
|
|
|
|
|
|
// This is a strict subset of the actual allowed identifiers
|
|
|
|
|
var validIdentifierRegexp = regexp.MustCompile("^[a-zA-Z_]\\w*$")
|
|
|
|
|
|
|
|
|
|
// Returns true if the given string is suitable as an identifier.
|
|
|
|
|
func validIdentifierName(name string) bool {
|
|
|
|
|
return validIdentifierRegexp.MatchString(name)
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 21:55:43 +01:00
|
|
|
//
|
2019-03-30 10:17:32 +01:00
|
|
|
//// Pseudo Column type returned by tableName.C(name)
|
2019-03-15 21:55:43 +01:00
|
|
|
//type deferredLookupColumn struct {
|
|
|
|
|
// isProjection
|
|
|
|
|
// isExpression
|
2019-03-30 10:17:32 +01:00
|
|
|
// tableName *Table
|
2019-03-15 21:55:43 +01:00
|
|
|
// colName string
|
|
|
|
|
//
|
|
|
|
|
// cachedColumn NonAliasColumn
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) Name() string {
|
|
|
|
|
// return c.colName
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) SerializeSqlForColumnList(
|
|
|
|
|
// out *bytes.Buffer) error {
|
|
|
|
|
//
|
|
|
|
|
// return c.SerializeSql(out)
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) SerializeSql(out *bytes.Buffer) error {
|
|
|
|
|
// if c.cachedColumn != nil {
|
|
|
|
|
// return c.cachedColumn.SerializeSql(out)
|
|
|
|
|
// }
|
|
|
|
|
//
|
2019-03-30 10:17:32 +01:00
|
|
|
// col, err := c.tableName.getColumn(c.colName)
|
2019-03-15 21:55:43 +01:00
|
|
|
// if err != nil {
|
|
|
|
|
// return err
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// c.cachedColumn = col
|
|
|
|
|
// return col.SerializeSql(out)
|
|
|
|
|
//}
|
|
|
|
|
//
|
2019-03-30 10:17:32 +01:00
|
|
|
//func (c *deferredLookupColumn) setTableName(tableName string) error {
|
2019-03-15 21:55:43 +01:00
|
|
|
// return errors.Newf(
|
|
|
|
|
// "Lookup column '%s' should never have setTableName called on it",
|
|
|
|
|
// c.colName)
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) Eq(rhs Expression) BoolExpression {
|
|
|
|
|
// lit, ok := rhs.(*literalExpression)
|
|
|
|
|
// if ok && sqltypes.Value(lit.value).IsNull() {
|
|
|
|
|
// return newBoolExpression(c, rhs, []byte(" IS "))
|
|
|
|
|
// }
|
|
|
|
|
// return newBoolExpression(c, rhs, []byte(" = "))
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) Gte(rhs Expression) BoolExpression {
|
|
|
|
|
// return Gte(c, rhs)
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) GteLiteral(rhs interface{}) BoolExpression {
|
|
|
|
|
// return Gte(c, Literal(rhs))
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) Lte(rhs Expression) BoolExpression {
|
|
|
|
|
// return Lte(c, rhs)
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) LteLiteral(literal interface{}) BoolExpression {
|
|
|
|
|
// return Lte(c, Literal(literal))
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) Asc() OrderByClause {
|
|
|
|
|
// return sqlbuilder.Asc(c)
|
|
|
|
|
//}
|
|
|
|
|
//
|
|
|
|
|
//func (c *deferredLookupColumn) Desc() OrderByClause {
|
|
|
|
|
// return sqlbuilder.Desc(c)
|
|
|
|
|
//}
|