From b10270b5025787c80ee74fdd2a12b04481cf174a Mon Sep 17 00:00:00 2001 From: go-jet Date: Thu, 18 Jul 2019 17:43:11 +0200 Subject: [PATCH] Additional documentation. --- README.md | 3 +- alias.go | 2 +- bool_expresion.go => bool_expression.go | 4 + cast.go | 2 + clause.go | 23 +- column.go | 26 +- column_types.go | 73 ++- date_expression.go | 4 + delete_statement.go | 9 +- doc.go | 4 +- enum_value.go | 1 + expression.go | 23 +- expression_table.go | 62 -- float_expression.go | 4 + func_expression.go | 832 +++++++++++++----------- func_expression_test.go | 12 - insert_statement.go | 15 +- insert_statement_test.go | 12 +- integer_expression.go | 4 + keyword.go | 3 + literal_expression.go | 14 +- lock_statement.go | 12 +- numeric_expression.go | 1 + operators.go | 22 +- operators_test.go | 1 + order_by_clause.go | 7 +- projection.go | 5 +- select_statement.go | 38 +- select_table.go | 63 ++ set_statement.go | 30 +- set_statement_test.go | 18 +- statement.go | 9 +- string_expression.go | 4 + table.go | 14 +- tests/all_types_test.go | 18 +- tests/delete_test.go | 8 +- tests/insert_test.go | 24 +- tests/sample_test.go | 4 +- tests/select_test.go | 78 +-- tests/update_test.go | 32 +- time_expression.go | 8 +- timestamp_expression.go | 4 + timestampz_expression.go | 4 + timez_expression.go | 12 + update_statement.go | 11 +- update_statement_test.go | 16 +- utils.go | 10 +- utils_test.go | 6 +- 48 files changed, 892 insertions(+), 699 deletions(-) rename bool_expresion.go => bool_expression.go (95%) delete mode 100644 expression_table.go create mode 100644 select_table.go diff --git a/README.md b/README.md index 8313dfd..b2b2408 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # Jet +[![Documentation](https://godoc.org/github.com/go-jet/jet?status.svg)](http://godoc.org/github.com/go-jet/jet) [![CircleCI](https://circleci.com/gh/go-jet/jet/tree/develop.svg?style=svg&circle-token=97f255c6a4a3ab6590ea2e9195eb3ebf9f97b4a7)](https://circleci.com/gh/go-jet/jet/tree/develop) Jet is a framework for writing type-safe SQL queries for PostgreSQL in Go, with ability to easily convert database query result to desired arbitrary structure. -_Support for additional databases will be added in future jet releases._ +_*Support for additional databases will be added in future jet releases._ ## Contents diff --git a/alias.go b/alias.go index 4f3b60a..29c021c 100644 --- a/alias.go +++ b/alias.go @@ -12,7 +12,7 @@ func newAlias(expression Expression, aliasName string) projection { } } -func (a *alias) from(subQuery ExpressionTable) projection { +func (a *alias) from(subQuery SelectTable) projection { column := newColumn(a.alias, "", nil) column.parent = &column column.subQuery = subQuery diff --git a/bool_expresion.go b/bool_expression.go similarity index 95% rename from bool_expresion.go rename to bool_expression.go index 9576d9b..d6b359b 100644 --- a/bool_expresion.go +++ b/bool_expression.go @@ -1,5 +1,6 @@ package jet +//BoolExpression interface type BoolExpression interface { Expression @@ -150,6 +151,9 @@ func newBoolExpressionWrap(expression Expression) BoolExpression { return &boolExpressionWrap } +// BoolExp is bool expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as bool expression. +// Does not add sql cast to generated sql builder output. func BoolExp(expression Expression) BoolExpression { return newBoolExpressionWrap(expression) } diff --git a/cast.go b/cast.go index 0f3a71f..b98af0f 100644 --- a/cast.go +++ b/cast.go @@ -36,6 +36,8 @@ type castImpl struct { castType string } +// CAST wraps expression for casting. +// For instance: CAST(table.column).AS_BOOL() func CAST(expression Expression) cast { return &castImpl{ Expression: expression, diff --git a/clause.go b/clause.go index d612079..7603a76 100644 --- a/clause.go +++ b/clause.go @@ -40,12 +40,12 @@ type sqlBuilder struct { type statementType string const ( - select_statement statementType = "SELECT" - insert_statement statementType = "INSERT" - update_statement statementType = "UPDATE" - delete_statement statementType = "DELETE" - set_statement statementType = "SET" - lock_statement statementType = "LOCK" + selectStatement statementType = "SELECT" + insertStatement statementType = "INSERT" + updateStatement statementType = "UPDATE" + deleteStatement statementType = "DELETE" + setStatement statementType = "SET" + lockStatement statementType = "LOCK" ) const defaultIdent = 5 @@ -102,7 +102,7 @@ func (q *sqlBuilder) writeGroupBy(statement statementType, groupBy []groupByClau return err } -func (q *sqlBuilder) writeOrderBy(statement statementType, orderBy []OrderByClause) error { +func (q *sqlBuilder) writeOrderBy(statement statementType, orderBy []orderByClause) error { q.newLine() q.writeString("ORDER BY") @@ -189,10 +189,10 @@ func (q *sqlBuilder) finalize() (string, []interface{}) { } func (q *sqlBuilder) insertConstantArgument(arg interface{}) { - q.writeString(ArgToString(arg)) + q.writeString(argToString(arg)) } -func (q *sqlBuilder) insertPreparedArgument(arg interface{}) { +func (q *sqlBuilder) insertParametrizedArgument(arg interface{}) { q.args = append(q.args, arg) argPlaceholder := "$" + strconv.Itoa(len(q.args)) @@ -204,7 +204,7 @@ func (q *sqlBuilder) reset() { q.args = []interface{}{} } -func ArgToString(value interface{}) string { +func argToString(value interface{}) string { if isNil(value) { return "NULL" } @@ -213,9 +213,8 @@ func ArgToString(value interface{}) string { case bool: if bindVal { return "TRUE" - } else { - return "FALSE" } + return "FALSE" case int8: return strconv.FormatInt(int64(bindVal), 10) case int: diff --git a/column.go b/column.go index 16dc2ae..f2a035c 100644 --- a/column.go +++ b/column.go @@ -7,10 +7,11 @@ type column interface { TableName() string setTableName(table string) - setSubQuery(subQuery ExpressionTable) + setSubQuery(subQuery SelectTable) defaultAlias() string } +// Column is common column interface for all types of columns. type Column interface { Expression column @@ -23,7 +24,7 @@ type columnImpl struct { name string tableName string - subQuery ExpressionTable + subQuery SelectTable } func newColumn(name string, tableName string, parent Column) columnImpl { @@ -49,7 +50,7 @@ func (c *columnImpl) setTableName(table string) { c.tableName = table } -func (c *columnImpl) setSubQuery(subQuery ExpressionTable) { +func (c *columnImpl) setSubQuery(subQuery SelectTable) { c.subQuery = subQuery } @@ -62,7 +63,7 @@ func (c *columnImpl) defaultAlias() string { } func (c *columnImpl) serializeForOrderBy(statement statementType, out *sqlBuilder) error { - if statement == set_statement { + if statement == setStatement { // set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause out.writeString(`"` + c.defaultAlias() + `"`) //always quote @@ -104,13 +105,13 @@ func (c columnImpl) serialize(statement statementType, out *sqlBuilder, options //------------------------------------------------------// -// Redefined type to support list of columns as projection +// ColumnList is redefined type to support list of columns as single projection type ColumnList []Column // projection interface implementation func (cl ColumnList) isProjectionType() {} -func (cl ColumnList) from(subQuery ExpressionTable) projection { +func (cl ColumnList) from(subQuery SelectTable) projection { newProjectionList := ProjectionList{} for _, column := range cl { @@ -134,8 +135,11 @@ func (cl ColumnList) serializeForProjection(statement statementType, out *sqlBui // dummy column interface implementation -func (cl ColumnList) Name() string { return "" } -func (cl ColumnList) TableName() string { return "" } -func (cl ColumnList) setTableName(name string) {} -func (cl ColumnList) setSubQuery(subQuery ExpressionTable) {} -func (cl ColumnList) defaultAlias() string { return "" } +// Name is placeholder for ColumnList to implement Column interface +func (cl ColumnList) Name() string { return "" } + +// TableName is placeholder for ColumnList to implement Column interface +func (cl ColumnList) TableName() string { return "" } +func (cl ColumnList) setTableName(name string) {} +func (cl ColumnList) setSubQuery(subQuery SelectTable) {} +func (cl ColumnList) defaultAlias() string { return "" } diff --git a/column_types.go b/column_types.go index f3a1f69..370942f 100644 --- a/column_types.go +++ b/column_types.go @@ -1,10 +1,11 @@ package jet +// ColumnBool is interface for SQL boolean columns. type ColumnBool interface { BoolExpression column - From(subQuery ExpressionTable) ColumnBool + From(subQuery SelectTable) ColumnBool } type boolColumnImpl struct { @@ -13,7 +14,7 @@ type boolColumnImpl struct { columnImpl } -func (i *boolColumnImpl) from(subQuery ExpressionTable) projection { +func (i *boolColumnImpl) from(subQuery SelectTable) projection { newBoolColumn := BoolColumn(i.name) newBoolColumn.setTableName(i.tableName) newBoolColumn.setSubQuery(subQuery) @@ -21,12 +22,13 @@ func (i *boolColumnImpl) from(subQuery ExpressionTable) projection { return newBoolColumn } -func (i *boolColumnImpl) From(subQuery ExpressionTable) ColumnBool { +func (i *boolColumnImpl) From(subQuery SelectTable) ColumnBool { newBoolColumn := i.from(subQuery).(ColumnBool) return newBoolColumn } +// BoolColumn creates named bool column. func BoolColumn(name string) ColumnBool { boolColumn := &boolColumnImpl{} boolColumn.columnImpl = newColumn(name, "", boolColumn) @@ -37,11 +39,12 @@ func BoolColumn(name string) ColumnBool { //------------------------------------------------------// +// ColumnFloat is interface for SQL real, numeric, decimal or double precision column. type ColumnFloat interface { FloatExpression column - From(subQuery ExpressionTable) ColumnFloat + From(subQuery SelectTable) ColumnFloat } type floatColumnImpl struct { @@ -49,7 +52,7 @@ type floatColumnImpl struct { columnImpl } -func (i *floatColumnImpl) from(subQuery ExpressionTable) projection { +func (i *floatColumnImpl) from(subQuery SelectTable) projection { newFloatColumn := FloatColumn(i.name) newFloatColumn.setTableName(i.tableName) newFloatColumn.setSubQuery(subQuery) @@ -57,12 +60,13 @@ func (i *floatColumnImpl) from(subQuery ExpressionTable) projection { return newFloatColumn } -func (i *floatColumnImpl) From(subQuery ExpressionTable) ColumnFloat { +func (i *floatColumnImpl) From(subQuery SelectTable) ColumnFloat { newFloatColumn := i.from(subQuery).(ColumnFloat) return newFloatColumn } +// FloatColumn creates named float column. func FloatColumn(name string) ColumnFloat { floatColumn := &floatColumnImpl{} floatColumn.floatInterfaceImpl.parent = floatColumn @@ -73,11 +77,12 @@ func FloatColumn(name string) ColumnFloat { //------------------------------------------------------// +// ColumnInteger is interface for SQL smallint, integer, bigint columns. type ColumnInteger interface { IntegerExpression column - From(subQuery ExpressionTable) ColumnInteger + From(subQuery SelectTable) ColumnInteger } type integerColumnImpl struct { @@ -86,7 +91,7 @@ type integerColumnImpl struct { columnImpl } -func (i *integerColumnImpl) from(subQuery ExpressionTable) projection { +func (i *integerColumnImpl) from(subQuery SelectTable) projection { newIntColumn := IntegerColumn(i.name) newIntColumn.setTableName(i.tableName) newIntColumn.setSubQuery(subQuery) @@ -94,10 +99,11 @@ func (i *integerColumnImpl) from(subQuery ExpressionTable) projection { return newIntColumn } -func (i *integerColumnImpl) From(subQuery ExpressionTable) ColumnInteger { +func (i *integerColumnImpl) From(subQuery SelectTable) ColumnInteger { return i.from(subQuery).(ColumnInteger) } +// IntegerColumn creates named integer column. func IntegerColumn(name string) ColumnInteger { integerColumn := &integerColumnImpl{} integerColumn.integerInterfaceImpl.parent = integerColumn @@ -108,11 +114,13 @@ func IntegerColumn(name string) ColumnInteger { //------------------------------------------------------// +// ColumnString is interface for SQL text, character, character varying +// bytea, uuid columns and enums types. type ColumnString interface { StringExpression column - From(subQuery ExpressionTable) ColumnString + From(subQuery SelectTable) ColumnString } type stringColumnImpl struct { @@ -121,7 +129,7 @@ type stringColumnImpl struct { columnImpl } -func (i *stringColumnImpl) from(subQuery ExpressionTable) projection { +func (i *stringColumnImpl) from(subQuery SelectTable) projection { newStrColumn := StringColumn(i.name) newStrColumn.setTableName(i.tableName) newStrColumn.setSubQuery(subQuery) @@ -129,10 +137,11 @@ func (i *stringColumnImpl) from(subQuery ExpressionTable) projection { return newStrColumn } -func (i *stringColumnImpl) From(subQuery ExpressionTable) ColumnString { +func (i *stringColumnImpl) From(subQuery SelectTable) ColumnString { return i.from(subQuery).(ColumnString) } +// StringColumn creates named string column. func StringColumn(name string) ColumnString { stringColumn := &stringColumnImpl{} stringColumn.stringInterfaceImpl.parent = stringColumn @@ -143,11 +152,12 @@ func StringColumn(name string) ColumnString { //------------------------------------------------------// +// ColumnTime is interface for SQL time column. type ColumnTime interface { TimeExpression column - From(subQuery ExpressionTable) ColumnTime + From(subQuery SelectTable) ColumnTime } type timeColumnImpl struct { @@ -155,7 +165,7 @@ type timeColumnImpl struct { columnImpl } -func (i *timeColumnImpl) from(subQuery ExpressionTable) projection { +func (i *timeColumnImpl) from(subQuery SelectTable) projection { newTimeColumn := TimeColumn(i.name) newTimeColumn.setTableName(i.tableName) newTimeColumn.setSubQuery(subQuery) @@ -163,10 +173,11 @@ func (i *timeColumnImpl) from(subQuery ExpressionTable) projection { return newTimeColumn } -func (i *timeColumnImpl) From(subQuery ExpressionTable) ColumnTime { +func (i *timeColumnImpl) From(subQuery SelectTable) ColumnTime { return i.from(subQuery).(ColumnTime) } +// TimeColumn creates named time column func TimeColumn(name string) ColumnTime { timeColumn := &timeColumnImpl{} timeColumn.timeInterfaceImpl.parent = timeColumn @@ -176,11 +187,12 @@ func TimeColumn(name string) ColumnTime { //------------------------------------------------------// +// ColumnTimez is interface of SQL time with time zone columns. type ColumnTimez interface { TimezExpression column - From(subQuery ExpressionTable) ColumnTimez + From(subQuery SelectTable) ColumnTimez } type timezColumnImpl struct { @@ -189,7 +201,7 @@ type timezColumnImpl struct { columnImpl } -func (i *timezColumnImpl) from(subQuery ExpressionTable) projection { +func (i *timezColumnImpl) from(subQuery SelectTable) projection { newTimezColumn := TimezColumn(i.name) newTimezColumn.setTableName(i.tableName) newTimezColumn.setSubQuery(subQuery) @@ -197,10 +209,11 @@ func (i *timezColumnImpl) from(subQuery ExpressionTable) projection { return newTimezColumn } -func (i *timezColumnImpl) From(subQuery ExpressionTable) ColumnTimez { +func (i *timezColumnImpl) From(subQuery SelectTable) ColumnTimez { return i.from(subQuery).(ColumnTimez) } +// TimezColumn creates named time with time zone column. func TimezColumn(name string) ColumnTimez { timezColumn := &timezColumnImpl{} timezColumn.timezInterfaceImpl.parent = timezColumn @@ -211,11 +224,12 @@ func TimezColumn(name string) ColumnTimez { //------------------------------------------------------// +// ColumnTimestamp is interface of SQL timestamp columns. type ColumnTimestamp interface { TimestampExpression column - From(subQuery ExpressionTable) ColumnTimestamp + From(subQuery SelectTable) ColumnTimestamp } type timestampColumnImpl struct { @@ -224,7 +238,7 @@ type timestampColumnImpl struct { columnImpl } -func (i *timestampColumnImpl) from(subQuery ExpressionTable) projection { +func (i *timestampColumnImpl) from(subQuery SelectTable) projection { newTimestampColumn := TimestampColumn(i.name) newTimestampColumn.setTableName(i.tableName) newTimestampColumn.setSubQuery(subQuery) @@ -232,10 +246,11 @@ func (i *timestampColumnImpl) from(subQuery ExpressionTable) projection { return newTimestampColumn } -func (i *timestampColumnImpl) From(subQuery ExpressionTable) ColumnTimestamp { +func (i *timestampColumnImpl) From(subQuery SelectTable) ColumnTimestamp { return i.from(subQuery).(ColumnTimestamp) } +// TimestampColumn creates named timestamp column func TimestampColumn(name string) ColumnTimestamp { timestampColumn := ×tampColumnImpl{} timestampColumn.timestampInterfaceImpl.parent = timestampColumn @@ -246,11 +261,12 @@ func TimestampColumn(name string) ColumnTimestamp { //------------------------------------------------------// +// ColumnTimestampz is interface of SQL timestamp with timezone columns. type ColumnTimestampz interface { TimestampzExpression column - From(subQuery ExpressionTable) ColumnTimestampz + From(subQuery SelectTable) ColumnTimestampz } type timestampzColumnImpl struct { @@ -259,7 +275,7 @@ type timestampzColumnImpl struct { columnImpl } -func (i *timestampzColumnImpl) from(subQuery ExpressionTable) projection { +func (i *timestampzColumnImpl) from(subQuery SelectTable) projection { newTimestampzColumn := TimestampzColumn(i.name) newTimestampzColumn.setTableName(i.tableName) newTimestampzColumn.setSubQuery(subQuery) @@ -267,10 +283,11 @@ func (i *timestampzColumnImpl) from(subQuery ExpressionTable) projection { return newTimestampzColumn } -func (i *timestampzColumnImpl) From(subQuery ExpressionTable) ColumnTimestampz { +func (i *timestampzColumnImpl) From(subQuery SelectTable) ColumnTimestampz { return i.from(subQuery).(ColumnTimestampz) } +// TimestampzColumn creates named timestamp with time zone column. func TimestampzColumn(name string) ColumnTimestampz { timestampzColumn := ×tampzColumnImpl{} timestampzColumn.timestampzInterfaceImpl.parent = timestampzColumn @@ -281,11 +298,12 @@ func TimestampzColumn(name string) ColumnTimestampz { //------------------------------------------------------// +// ColumnDate is interface of SQL date columns. type ColumnDate interface { DateExpression column - From(subQuery ExpressionTable) ColumnDate + From(subQuery SelectTable) ColumnDate } type dateColumnImpl struct { @@ -294,7 +312,7 @@ type dateColumnImpl struct { columnImpl } -func (i *dateColumnImpl) from(subQuery ExpressionTable) projection { +func (i *dateColumnImpl) from(subQuery SelectTable) projection { newDateColumn := DateColumn(i.name) newDateColumn.setTableName(i.tableName) newDateColumn.setSubQuery(subQuery) @@ -302,10 +320,11 @@ func (i *dateColumnImpl) from(subQuery ExpressionTable) projection { return newDateColumn } -func (i *dateColumnImpl) From(subQuery ExpressionTable) ColumnDate { +func (i *dateColumnImpl) From(subQuery SelectTable) ColumnDate { return i.from(subQuery).(ColumnDate) } +// DateColumn creates named date column. func DateColumn(name string) ColumnDate { dateColumn := &dateColumnImpl{} dateColumn.dateInterfaceImpl.parent = dateColumn diff --git a/date_expression.go b/date_expression.go index 77edb03..0c2b0ed 100644 --- a/date_expression.go +++ b/date_expression.go @@ -1,5 +1,6 @@ package jet +// DateExpression is interface for all SQL date expressions. type DateExpression interface { Expression @@ -63,6 +64,9 @@ func newDateExpressionWrap(expression Expression) DateExpression { return &dateExpressionWrap } +// DateExp is date expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as date expression. +// Does not add sql cast to generated sql builder output. func DateExp(expression Expression) DateExpression { return newDateExpressionWrap(expression) } diff --git a/delete_statement.go b/delete_statement.go index c328953..8e9447a 100644 --- a/delete_statement.go +++ b/delete_statement.go @@ -7,6 +7,7 @@ import ( "github.com/go-jet/jet/execution" ) +// DeleteStatement is interface for SQL DELETE statement type DeleteStatement interface { Statement @@ -48,7 +49,7 @@ func (d *deleteStatementImpl) serializeImpl(out *sqlBuilder) error { return errors.New("jet: nil tableName") } - if err := d.table.serialize(delete_statement, out); err != nil { + if err := d.table.serialize(deleteStatement, out); err != nil { return err } @@ -56,11 +57,11 @@ func (d *deleteStatementImpl) serializeImpl(out *sqlBuilder) error { return errors.New("jet: deleting without a WHERE clause") } - if err := out.writeWhere(delete_statement, d.where); err != nil { + if err := out.writeWhere(deleteStatement, d.where); err != nil { return err } - if err := out.writeReturning(delete_statement, d.returning); err != nil { + if err := out.writeReturning(deleteStatement, d.returning); err != nil { return err } @@ -89,7 +90,7 @@ func (d *deleteStatementImpl) Query(db execution.DB, destination interface{}) er } func (d *deleteStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error { - return queryContext(d, db, context, destination) + return queryContext(context, d, db, destination) } func (d *deleteStatementImpl) Exec(db execution.DB) (res sql.Result, err error) { diff --git a/doc.go b/doc.go index 2a48dca..5062a78 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ /* - Package Jet is a framework for writing type-safe SQL queries for PostgreSQL in Go, with ability - to easily convert database query result to desired arbitrary structure. +Package jet is a framework for writing type-safe SQL queries for PostgreSQL in Go, with ability +to easily convert database query result to desired arbitrary structure. */ package jet diff --git a/enum_value.go b/enum_value.go index 49fe798..7691f96 100644 --- a/enum_value.go +++ b/enum_value.go @@ -6,6 +6,7 @@ type enumValue struct { name string } +// NewEnumValue creates new named enum value func NewEnumValue(name string) StringExpression { enumValue := &enumValue{name: name} diff --git a/expression.go b/expression.go index 6d90355..81fc67e 100644 --- a/expression.go +++ b/expression.go @@ -4,12 +4,13 @@ import ( "errors" ) -// Common expression interface +// Expression is common interface for all expressions. +// Can be Bool, Int, Float, String, Date, Time, Timez, Timestamp or Timestampz expressions. type Expression interface { clause projection groupByClause - OrderByClause + orderByClause // Test expression whether it is a NULL value. IS_NULL() BoolExpression @@ -25,16 +26,16 @@ type Expression interface { AS(alias string) projection // Expression will be used to sort query result in ascending order - ASC() OrderByClause + ASC() orderByClause // Expression will be used to sort query result in ascending order - DESC() OrderByClause + DESC() orderByClause } type expressionInterfaceImpl struct { parent Expression } -func (e *expressionInterfaceImpl) from(subQuery ExpressionTable) projection { +func (e *expressionInterfaceImpl) from(subQuery SelectTable) projection { return e.parent } @@ -58,11 +59,11 @@ func (e *expressionInterfaceImpl) AS(alias string) projection { return newAlias(e.parent, alias) } -func (e *expressionInterfaceImpl) ASC() OrderByClause { +func (e *expressionInterfaceImpl) ASC() orderByClause { return newOrderByClause(e.parent, true) } -func (e *expressionInterfaceImpl) DESC() OrderByClause { +func (e *expressionInterfaceImpl) DESC() orderByClause { return newOrderByClause(e.parent, false) } @@ -145,13 +146,13 @@ func newPrefixExpression(expression Expression, operator string) prefixOpExpress func (p *prefixOpExpression) serialize(statement statementType, out *sqlBuilder, options ...serializeOption) error { if p == nil { - return errors.New("jet: Prefix Expression is nil.") + return errors.New("jet: Prefix Expression is nil") } out.writeString(p.operator + " ") if p.expression == nil { - return errors.New("jet: nil prefix Expression.") + return errors.New("jet: nil prefix Expression") } if err := p.expression.serialize(statement, out); err != nil { return err @@ -177,11 +178,11 @@ func newPostfixOpExpression(expression Expression, operator string) postfixOpExp func (p *postfixOpExpression) serialize(statement statementType, out *sqlBuilder, options ...serializeOption) error { if p == nil { - return errors.New("jet: Postifx operator Expression is nil.") + return errors.New("jet: Postifx operator Expression is nil") } if p.expression == nil { - return errors.New("jet: nil prefix Expression.") + return errors.New("jet: nil prefix Expression") } if err := p.expression.serialize(statement, out); err != nil { return err diff --git a/expression_table.go b/expression_table.go deleted file mode 100644 index e443660..0000000 --- a/expression_table.go +++ /dev/null @@ -1,62 +0,0 @@ -package jet - -import "errors" - -type ExpressionTable interface { - ReadableTable - - Alias() string - - AllColumns() ProjectionList -} - -type expressionTableImpl struct { - readableTableInterfaceImpl - expression Expression - alias string - - projections []projection -} - -func newExpressionTable(expression Expression, alias string, projections []projection) ExpressionTable { - expTable := &expressionTableImpl{expression: expression, alias: alias} - - expTable.readableTableInterfaceImpl.parent = expTable - - for _, projection := range projections { - newProjection := projection.from(expTable) - - expTable.projections = append(expTable.projections, newProjection) - } - - return expTable -} - -func (e *expressionTableImpl) Alias() string { - return e.alias -} - -func (e *expressionTableImpl) columns() []column { - return nil -} - -func (e *expressionTableImpl) AllColumns() ProjectionList { - return e.projections -} - -func (e *expressionTableImpl) serialize(statement statementType, out *sqlBuilder, options ...serializeOption) error { - if e == nil { - return errors.New("jet: Expression table is nil. ") - } - - err := e.expression.serialize(statement, out) - - if err != nil { - return err - } - - out.writeString("AS") - out.writeIdentifier(e.alias) - - return nil -} diff --git a/float_expression.go b/float_expression.go index dcfa530..c9e4b9f 100644 --- a/float_expression.go +++ b/float_expression.go @@ -1,5 +1,6 @@ package jet +//FloatExpression is interface for SQL float columns type FloatExpression interface { Expression numericExpression @@ -115,6 +116,9 @@ func newFloatExpressionWrap(expression Expression) FloatExpression { return &floatExpressionWrap } +// FloatExp is date expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as float expression. +// Does not add sql cast to generated sql builder output. func FloatExp(expression Expression) FloatExpression { return newFloatExpressionWrap(expression) } diff --git a/func_expression.go b/func_expression.go index 447d249..835cfe7 100644 --- a/func_expression.go +++ b/func_expression.go @@ -2,6 +2,466 @@ package jet import "errors" +// ROW is construct one table row from list of expressions. +func ROW(expressions ...Expression) Expression { + return newFunc("ROW", expressions, nil) +} + +// ------------------ Mathematical functions ---------------// + +// ABSf calculates absolute value from float expression +func ABSf(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("ABS", floatExpression) +} + +// ABSi calculates absolute value from int expression +func ABSi(integerExpression IntegerExpression) IntegerExpression { + return newIntegerFunc("ABS", integerExpression) +} + +// SQRT calculates square root of numeric expression +func SQRT(numericExpression NumericExpression) FloatExpression { + return newFloatFunc("SQRT", numericExpression) +} + +// CBRT calculates cube root of numeric expression +func CBRT(numericExpression NumericExpression) FloatExpression { + return newFloatFunc("CBRT", numericExpression) +} + +// CEIL calculates ceil of float expression +func CEIL(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("CEIL", floatExpression) +} + +// FLOOR calculates floor of float expression +func FLOOR(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("FLOOR", floatExpression) +} + +// ROUND calculates round of a float expressions with optional precision +func ROUND(floatExpression FloatExpression, precision ...IntegerExpression) FloatExpression { + if len(precision) > 0 { + return newFloatFunc("ROUND", floatExpression, precision[0]) + } + return newFloatFunc("ROUND", floatExpression) +} + +// SIGN returns sign of float expression +func SIGN(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("SIGN", floatExpression) +} + +// TRUNC calculates trunc of float expression with optional precision +func TRUNC(floatExpression FloatExpression, precision ...IntegerExpression) FloatExpression { + if len(precision) > 0 { + return newFloatFunc("TRUNC", floatExpression, precision[0]) + } + return newFloatFunc("TRUNC", floatExpression) +} + +// LN calculates natural algorithm of float expression +func LN(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("LN", floatExpression) +} + +// LOG calculates logarithm of float expression +func LOG(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("LOG", floatExpression) +} + +// ----------------- Aggregate functions -------------------// + +// AVG is aggregate function used to calculate avg value from numeric expression +func AVG(numericExpression NumericExpression) FloatExpression { + return newFloatFunc("AVG", numericExpression) +} + +// BIT_AND is aggregate function used to calculates the bitwise AND of all non-null input values, or null if none. +func BIT_AND(integerExpression IntegerExpression) IntegerExpression { + return newIntegerFunc("BIT_AND", integerExpression) +} + +// BIT_OR is aggregate function used to calculates the bitwise OR of all non-null input values, or null if none. +func BIT_OR(integerExpression IntegerExpression) IntegerExpression { + return newIntegerFunc("BIT_OR", integerExpression) +} + +// BOOL_AND is aggregate function. Returns true if all input values are true, otherwise false +func BOOL_AND(boolExpression BoolExpression) BoolExpression { + return newBoolFunc("BOOL_AND", boolExpression) +} + +// BOOL_OR is aggregate function. Returns true if at least one input value is true, otherwise false +func BOOL_OR(boolExpression BoolExpression) BoolExpression { + return newBoolFunc("BOOL_OR", boolExpression) +} + +// COUNT is aggregate function. Returns number of input rows for which the value of expression is not null. +func COUNT(expression Expression) IntegerExpression { + return newIntegerFunc("COUNT", expression) +} + +// EVERY is aggregate function. Returns true if all input values are true, otherwise false +func EVERY(boolExpression BoolExpression) BoolExpression { + return newBoolFunc("EVERY", boolExpression) +} + +// MAXf is aggregate function. Returns maximum value of float expression across all input values +func MAXf(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("MAX", floatExpression) +} + +// MAXi is aggregate function. Returns maximum value of int expression across all input values +func MAXi(integerExpression IntegerExpression) IntegerExpression { + return newIntegerFunc("MAX", integerExpression) +} + +// MINf is aggregate function. Returns minimum value of float expression across all input values +func MINf(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("MIN", floatExpression) +} + +// MINi is aggregate function. Returns minimum value of int expression across all input values +func MINi(integerExpression IntegerExpression) IntegerExpression { + return newIntegerFunc("MIN", integerExpression) +} + +// SUMf is aggregate function. Returns sum of expression across all float expressions +func SUMf(floatExpression FloatExpression) FloatExpression { + return newFloatFunc("SUM", floatExpression) +} + +// SUMi is aggregate function. Returns sum of expression across all integer expression. +func SUMi(integerExpression IntegerExpression) IntegerExpression { + return newIntegerFunc("SUM", integerExpression) +} + +//------------ String functions ------------------// + +// BIT_LENGTH returns number of bits in string expression +func BIT_LENGTH(stringExpression StringExpression) IntegerExpression { + return newIntegerFunc("BIT_LENGTH", stringExpression) +} + +// CHAR_LENGTH returns number of characters in string expression +func CHAR_LENGTH(stringExpression StringExpression) IntegerExpression { + return newIntegerFunc("CHAR_LENGTH", stringExpression) +} + +// OCTET_LENGTH returns number of bytes in string expression +func OCTET_LENGTH(stringExpression StringExpression) IntegerExpression { + return newIntegerFunc("OCTET_LENGTH", stringExpression) +} + +// LOWER returns string expression in lower case +func LOWER(stringExpression StringExpression) StringExpression { + return newStringFunc("LOWER", stringExpression) +} + +// UPPER returns string expression in upper case +func UPPER(stringExpression StringExpression) StringExpression { + return newStringFunc("UPPER", stringExpression) +} + +// BTRIM removes the longest string consisting only of characters +// in characters (a space by default) from the start and end of string +func BTRIM(stringExpression StringExpression, trimChars ...StringExpression) StringExpression { + if len(trimChars) > 0 { + return newStringFunc("LTRIM", stringExpression, trimChars[0]) + } + return newStringFunc("BTRIM", stringExpression) +} + +// LTRIM removes the longest string containing only characters +// from characters (a space by default) from the start of string +func LTRIM(str StringExpression, trimChars ...StringExpression) StringExpression { + if len(trimChars) > 0 { + return newStringFunc("LTRIM", str, trimChars[0]) + } + return newStringFunc("LTRIM", str) +} + +// RTRIM removes the longest string containing only characters +// from characters (a space by default) from the end of string +func RTRIM(str StringExpression, trimChars ...StringExpression) StringExpression { + if len(trimChars) > 0 { + return newStringFunc("RTRIM", str, trimChars[0]) + } + return newStringFunc("RTRIM", str) +} + +// CHR returns character with the given code. +func CHR(integerExpression IntegerExpression) StringExpression { + return newStringFunc("CHR", integerExpression) +} + +// +//func CONCAT(expressions ...Expression) StringExpression { +// return newStringFunc("CONCAT", expressions...) +//} +// +//func CONCAT_WS(expressions ...Expression) StringExpression { +// return newStringFunc("CONCAT_WS", expressions...) +//} + +// CONVERT converts string to dest_encoding. The original encoding is +// specified by src_encoding. The string must be valid in this encoding. +func CONVERT(str StringExpression, srcEncoding StringExpression, destEncoding StringExpression) StringExpression { + return newStringFunc("CONVERT", str, srcEncoding, destEncoding) +} + +// CONVERT_FROM converts string to the database encoding. The original +// encoding is specified by src_encoding. The string must be valid in this encoding. +func CONVERT_FROM(str StringExpression, srcEncoding StringExpression) StringExpression { + return newStringFunc("CONVERT_FROM", str, srcEncoding) +} + +// CONVERT_TO converts string to dest_encoding. +func CONVERT_TO(str StringExpression, toEncoding StringExpression) StringExpression { + return newStringFunc("CONVERT_TO", str, toEncoding) +} + +// ENCODE encodes binary data into a textual representation. +// Supported formats are: base64, hex, escape. escape converts zero bytes and +// high-bit-set bytes to octal sequences (\nnn) and doubles backslashes. +func ENCODE(data StringExpression, format StringExpression) StringExpression { + return newStringFunc("ENCODE", data, format) +} + +// DECODE decodes binary data from textual representation in string. +// Options for format are same as in encode. +func DECODE(data StringExpression, format StringExpression) StringExpression { + return newStringFunc("DECODE", data, format) +} + +//func FORMAT(formatStr StringExpression, formatArgs ...expressions) StringExpression { +// args := []expressions{formatStr} +// args = append(args, formatArgs...) +// return newStringFunc("FORMAT", args...) +//} + +// INITCAP converts the first letter of each word to upper case +// and the rest to lower case. Words are sequences of alphanumeric +// characters separated by non-alphanumeric characters. +func INITCAP(str StringExpression) StringExpression { + return newStringFunc("INITCAP", str) +} + +// LEFT returns first n characters in the string. +// When n is negative, return all but last |n| characters. +func LEFT(str StringExpression, n IntegerExpression) StringExpression { + return newStringFunc("LEFT", str, n) +} + +// RIGHT returns last n characters in the string. +// When n is negative, return all but first |n| characters. +func RIGHT(str StringExpression, n IntegerExpression) StringExpression { + return newStringFunc("RIGHT", str, n) +} + +// LENGTH returns number of characters in string with a given encoding +func LENGTH(str StringExpression, encoding ...StringExpression) StringExpression { + if len(encoding) > 0 { + return newStringFunc("LENGTH", str, encoding[0]) + } + return newStringFunc("LENGTH", str) +} + +// LPAD fills up the string to length length by prepending the characters +// fill (a space by default). If the string is already longer than length +// then it is truncated (on the right). +func LPAD(str StringExpression, length IntegerExpression, text ...StringExpression) StringExpression { + if len(text) > 0 { + return newStringFunc("LPAD", str, length, text[0]) + } + + return newStringFunc("LPAD", str, length) +} + +// RPAD fills up the string to length length by appending the characters +// fill (a space by default). If the string is already longer than length then it is truncated. +func RPAD(str StringExpression, length IntegerExpression, text ...StringExpression) StringExpression { + if len(text) > 0 { + return newStringFunc("RPAD", str, length, text[0]) + } + + return newStringFunc("RPAD", str, length) +} + +// MD5 calculates the MD5 hash of string, returning the result in hexadecimal +func MD5(stringExpression StringExpression) StringExpression { + return newStringFunc("MD5", stringExpression) +} + +// REPEAT repeats string the specified number of times +func REPEAT(str StringExpression, n IntegerExpression) StringExpression { + return newStringFunc("REPEAT", str, n) +} + +// REPLACE replaces all occurrences in string of substring from with substring to +func REPLACE(text, from, to StringExpression) StringExpression { + return newStringFunc("REPLACE", text, from, to) +} + +// REVERSE returns reversed string. +func REVERSE(stringExpression StringExpression) StringExpression { + return newStringFunc("REVERSE", stringExpression) +} + +// STRPOS returns location of specified substring (same as position(substring in string), +// but note the reversed argument order) +func STRPOS(str, substring StringExpression) IntegerExpression { + return newIntegerFunc("STRPOS", str, substring) +} + +// SUBSTR extracts substring +func SUBSTR(str StringExpression, from IntegerExpression, count ...IntegerExpression) StringExpression { + if len(count) > 0 { + return newStringFunc("SUBSTR", str, from, count[0]) + } + return newStringFunc("SUBSTR", str, from) +} + +// TO_ASCII convert string to ASCII from another encoding +func TO_ASCII(str StringExpression, encoding ...StringExpression) StringExpression { + if len(encoding) > 0 { + return newStringFunc("TO_ASCII", str, encoding[0]) + } + return newStringFunc("TO_ASCII", str) +} + +// TO_HEX converts number to its equivalent hexadecimal representation +func TO_HEX(number IntegerExpression) StringExpression { + return newStringFunc("TO_HEX", number) +} + +//----------Data Type Formatting Functions ----------------------// + +// TO_CHAR converts expression to string with format +func TO_CHAR(expression Expression, format StringExpression) StringExpression { + return newStringFunc("TO_CHAR", expression, format) +} + +// TO_DATE converts string to date using format +func TO_DATE(dateStr, format StringExpression) DateExpression { + return newDateFunc("TO_DATE", dateStr, format) +} + +// TO_NUMBER converst string to numeric using format +func TO_NUMBER(floatStr, format StringExpression) FloatExpression { + return newFloatFunc("TO_NUMBER", floatStr, format) +} + +// TO_TIMESTAMP converst string to time stamp with time zone using format +func TO_TIMESTAMP(timestampzStr, format StringExpression) TimestampzExpression { + return newTimestampzFunc("TO_TIMESTAMP", timestampzStr, format) +} + +//----------------- Date/Time Functions and Operators ---------------// + +// CURRENT_DATE returns current date +func CURRENT_DATE() DateExpression { + dateFunc := newDateFunc("CURRENT_DATE") + dateFunc.noBrackets = true + return dateFunc +} + +// CURRENT_TIME returns current time with time zone +func CURRENT_TIME(precision ...int) TimezExpression { + var timezFunc *timezFunc + + if len(precision) > 0 { + timezFunc = newTimezFunc("CURRENT_TIME", constLiteral(precision[0])) + } else { + timezFunc = newTimezFunc("CURRENT_TIME") + } + + timezFunc.noBrackets = true + + return timezFunc +} + +// CURRENT_TIMESTAMP returns current timestamp with time zone +func CURRENT_TIMESTAMP(precision ...int) TimestampzExpression { + var timestampzFunc *timestampzFunc + + if len(precision) > 0 { + timestampzFunc = newTimestampzFunc("CURRENT_TIMESTAMP", constLiteral(precision[0])) + } else { + timestampzFunc = newTimestampzFunc("CURRENT_TIMESTAMP") + } + + timestampzFunc.noBrackets = true + + return timestampzFunc +} + +// LOCALTIME returns local time of day using optional precision +func LOCALTIME(precision ...int) TimeExpression { + var timeFunc *timeFunc + + if len(precision) > 0 { + timeFunc = newTimeFunc("LOCALTIME", constLiteral(precision[0])) + } else { + timeFunc = newTimeFunc("LOCALTIME") + } + + timeFunc.noBrackets = true + + return timeFunc +} + +// LOCALTIMESTAMP returns current date and time using optional precision +func LOCALTIMESTAMP(precision ...int) TimestampExpression { + var timestampFunc *timestampFunc + + if len(precision) > 0 { + timestampFunc = newTimestampFunc("LOCALTIMESTAMP", constLiteral(precision[0])) + } else { + timestampFunc = newTimestampFunc("LOCALTIMESTAMP") + } + + timestampFunc.noBrackets = true + + return timestampFunc +} + +// NOW returns current date and time +func NOW() TimestampzExpression { + return newTimestampzFunc("NOW") +} + +// --------------- Conditional Expressions Functions -------------// + +// COALESCE function returns the first of its arguments that is not null. +func COALESCE(value Expression, values ...Expression) Expression { + var allValues = []Expression{value} + allValues = append(allValues, values...) + return newFunc("COALESCE", allValues, nil) +} + +// NULLIF function returns a null value if value1 equals value2; otherwise it returns value1. +func NULLIF(value1, value2 Expression) Expression { + return newFunc("NULLIF", []Expression{value1, value2}, nil) +} + +// GREATEST selects the largest value from a list of expressions +func GREATEST(value Expression, values ...Expression) Expression { + var allValues = []Expression{value} + allValues = append(allValues, values...) + return newFunc("GREATEST", allValues, nil) +} + +// LEAST selects the smallest value from a list of expressions +func LEAST(value Expression, values ...Expression) Expression { + var allValues = []Expression{value} + allValues = append(allValues, values...) + return newFunc("LEAST", allValues, nil) +} + +//--------------------------------------------------------------------// + type funcExpressionImpl struct { expressionInterfaceImpl @@ -175,375 +635,3 @@ func newTimestampzFunc(name string, expressions ...Expression) *timestampzFunc { return timestampzFunc } - -func ROW(expressions ...Expression) Expression { - return newFunc("ROW", expressions, nil) -} - -// ------------------ Mathematical functions ---------------// - -func ABSf(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("ABS", floatExpression) -} - -func ABSi(integerExpression IntegerExpression) IntegerExpression { - return newIntegerFunc("ABS", integerExpression) -} - -func SQRT(numericExpression NumericExpression) FloatExpression { - return newFloatFunc("SQRT", numericExpression) -} - -func CBRT(numericExpression NumericExpression) FloatExpression { - return newFloatFunc("CBRT", numericExpression) -} - -func CEIL(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("CEIL", floatExpression) -} - -func FLOOR(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("FLOOR", floatExpression) -} - -func ROUND(floatExpression FloatExpression, intExpression ...IntegerExpression) FloatExpression { - if len(intExpression) > 0 { - return newFloatFunc("ROUND", floatExpression, intExpression[0]) - } - return newFloatFunc("ROUND", floatExpression) -} - -func SIGN(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("SIGN", floatExpression) -} - -func TRUNC(floatExpression FloatExpression, intExpression ...IntegerExpression) FloatExpression { - if len(intExpression) > 0 { - return newFloatFunc("TRUNC", floatExpression, intExpression[0]) - } - return newFloatFunc("TRUNC", floatExpression) -} - -func LN(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("LN", floatExpression) -} - -func LOG(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("LOG", floatExpression) -} - -// ----------------- Aggregate functions -------------------// - -func AVG(numericExpression NumericExpression) FloatExpression { - return newFloatFunc("AVG", numericExpression) -} - -func BIT_AND(integerExpression IntegerExpression) IntegerExpression { - return newIntegerFunc("BIT_AND", integerExpression) -} - -func BIT_OR(integerExpression IntegerExpression) IntegerExpression { - return newIntegerFunc("BIT_OR", integerExpression) -} - -func BOOL_AND(boolExpression BoolExpression) BoolExpression { - return newBoolFunc("BOOL_AND", boolExpression) -} - -func BOOL_OR(boolExpression BoolExpression) BoolExpression { - return newBoolFunc("BOOL_OR", boolExpression) -} - -func COUNT(expression Expression) IntegerExpression { - return newIntegerFunc("COUNT", expression) -} - -func EVERY(boolExpression BoolExpression) BoolExpression { - return newBoolFunc("EVERY", boolExpression) -} - -func MAXf(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("MAX", floatExpression) -} - -func MAXi(integerExpression IntegerExpression) IntegerExpression { - return newIntegerFunc("MAX", integerExpression) -} - -func MINf(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("MIN", floatExpression) -} - -func MINi(integerExpression IntegerExpression) IntegerExpression { - return newIntegerFunc("MIN", integerExpression) -} - -func SUMf(floatExpression FloatExpression) FloatExpression { - return newFloatFunc("SUM", floatExpression) -} - -func SUMi(integerExpression IntegerExpression) IntegerExpression { - return newIntegerFunc("SUM", integerExpression) -} - -//------------ String functions ------------------// - -func BIT_LENGTH(stringExpression StringExpression) IntegerExpression { - return newIntegerFunc("BIT_LENGTH", stringExpression) -} - -func CHAR_LENGTH(stringExpression StringExpression) IntegerExpression { - return newIntegerFunc("CHAR_LENGTH", stringExpression) -} - -func OCTET_LENGTH(stringExpression StringExpression) IntegerExpression { - return newIntegerFunc("OCTET_LENGTH", stringExpression) -} - -func LOWER(stringExpression StringExpression) StringExpression { - return newStringFunc("LOWER", stringExpression) -} - -func UPPER(stringExpression StringExpression) StringExpression { - return newStringFunc("UPPER", stringExpression) -} - -func BTRIM(stringExpression StringExpression) StringExpression { - return newStringFunc("BTRIM", stringExpression) -} - -func LTRIM(str StringExpression, trimChars ...StringExpression) StringExpression { - if len(trimChars) > 0 { - return newStringFunc("LTRIM", str, trimChars[0]) - } - return newStringFunc("LTRIM", str) -} - -func RTRIM(str StringExpression, trimChars ...StringExpression) StringExpression { - if len(trimChars) > 0 { - return newStringFunc("RTRIM", str, trimChars[0]) - } - return newStringFunc("RTRIM", str) -} - -func CHR(integerExpression IntegerExpression) StringExpression { - return newStringFunc("CHR", integerExpression) -} - -// -//func CONCAT(expressions ...Expression) StringExpression { -// return newStringFunc("CONCAT", expressions...) -//} -// -//func CONCAT_WS(expressions ...Expression) StringExpression { -// return newStringFunc("CONCAT_WS", expressions...) -//} - -func CONVERT(str StringExpression, fromEncoding StringExpression, toEncoding StringExpression) StringExpression { - return newStringFunc("CONVERT", str, fromEncoding, toEncoding) -} - -func CONVERT_FROM(str StringExpression, fromEncoding StringExpression) StringExpression { - return newStringFunc("CONVERT_FROM", str, fromEncoding) -} - -func CONVERT_TO(str StringExpression, toEncoding StringExpression) StringExpression { - return newStringFunc("CONVERT_TO", str, toEncoding) -} - -func ENCODE(data StringExpression, format StringExpression) StringExpression { - return newStringFunc("ENCODE", data, format) -} - -func DECODE(data StringExpression, format StringExpression) StringExpression { - return newStringFunc("DECODE", data, format) -} - -//func FORMAT(formatStr StringExpression, formatArgs ...expressions) StringExpression { -// args := []expressions{formatStr} -// args = append(args, formatArgs...) -// return newStringFunc("FORMAT", args...) -//} - -func INITCAP(str StringExpression) StringExpression { - return newStringFunc("INITCAP", str) -} - -func LEFT(str StringExpression, n IntegerExpression) StringExpression { - return newStringFunc("LEFT", str, n) -} - -func RIGHT(str StringExpression, n IntegerExpression) StringExpression { - return newStringFunc("RIGHT", str, n) -} - -func LENGTH(str StringExpression, encoding ...StringExpression) StringExpression { - if len(encoding) > 0 { - return newStringFunc("LENGTH", str, encoding[0]) - } - return newStringFunc("LENGTH", str) -} - -func LPAD(str StringExpression, length IntegerExpression, text ...StringExpression) StringExpression { - if len(text) > 0 { - return newStringFunc("LPAD", str, length, text[0]) - } - - return newStringFunc("LPAD", str, length) -} - -func RPAD(str StringExpression, length IntegerExpression, text ...StringExpression) StringExpression { - if len(text) > 0 { - return newStringFunc("RPAD", str, length, text[0]) - } - - return newStringFunc("RPAD", str, length) -} - -func MD5(stringExpression StringExpression) StringExpression { - return newStringFunc("MD5", stringExpression) -} - -func REPEAT(str StringExpression, n IntegerExpression) StringExpression { - return newStringFunc("REPEAT", str, n) -} - -func REPLACE(text, from, to StringExpression) StringExpression { - return newStringFunc("REPLACE", text, from, to) -} - -func REVERSE(stringExpression StringExpression) StringExpression { - return newStringFunc("REVERSE", stringExpression) -} - -func STRPOS(str, substring StringExpression) IntegerExpression { - return newIntegerFunc("STRPOS", str, substring) -} - -func SUBSTR(str StringExpression, from IntegerExpression, count ...IntegerExpression) StringExpression { - if len(count) > 0 { - return newStringFunc("SUBSTR", str, from, count[0]) - } - return newStringFunc("SUBSTR", str, from) -} - -func TO_ASCII(str StringExpression, encoding ...StringExpression) StringExpression { - if len(encoding) > 0 { - return newStringFunc("TO_ASCII", str, encoding[0]) - } - return newStringFunc("TO_ASCII", str) -} - -func TO_HEX(number IntegerExpression) StringExpression { - return newStringFunc("TO_HEX", number) -} - -//----------Data Type Formatting Functions ----------------------// - -func TO_CHAR(expression Expression, text StringExpression) StringExpression { - return newStringFunc("TO_CHAR", expression, text) -} - -func TO_DATE(dateStr, format StringExpression) DateExpression { - return newDateFunc("TO_DATE", dateStr, format) -} - -func TO_NUMBER(floatStr, format StringExpression) FloatExpression { - return newFloatFunc("TO_NUMBER", floatStr, format) -} - -func TO_TIMESTAMP(timestampzStr, format StringExpression) TimestampzExpression { - return newTimestampzFunc("TO_TIMESTAMP", timestampzStr, format) -} - -//----------------- Date/Time Functions and Operators ---------------// - -func CURRENT_DATE() DateExpression { - dateFunc := newDateFunc("CURRENT_DATE") - dateFunc.noBrackets = true - return dateFunc -} - -func CURRENT_TIME(precision ...int) TimezExpression { - var timezFunc *timezFunc - - if len(precision) > 0 { - timezFunc = newTimezFunc("CURRENT_TIME", constLiteral(precision[0])) - } else { - timezFunc = newTimezFunc("CURRENT_TIME") - } - - timezFunc.noBrackets = true - - return timezFunc -} - -func CURRENT_TIMESTAMP(precision ...int) TimestampzExpression { - var timestampzFunc *timestampzFunc - - if len(precision) > 0 { - timestampzFunc = newTimestampzFunc("CURRENT_TIMESTAMP", constLiteral(precision[0])) - } else { - timestampzFunc = newTimestampzFunc("CURRENT_TIMESTAMP") - } - - timestampzFunc.noBrackets = true - - return timestampzFunc -} - -func LOCALTIME(precision ...int) TimeExpression { - var timeFunc *timeFunc - - if len(precision) > 0 { - timeFunc = newTimeFunc("LOCALTIME", constLiteral(precision[0])) - } else { - timeFunc = newTimeFunc("LOCALTIME") - } - - timeFunc.noBrackets = true - - return timeFunc -} - -func LOCALTIMESTAMP(precision ...int) TimestampExpression { - var timestampFunc *timestampFunc - - if len(precision) > 0 { - timestampFunc = newTimestampFunc("LOCALTIMESTAMP", constLiteral(precision[0])) - } else { - timestampFunc = newTimestampFunc("LOCALTIMESTAMP") - } - - timestampFunc.noBrackets = true - - return timestampFunc -} - -func NOW() TimestampzExpression { - return newTimestampzFunc("NOW") -} - -// --------------- Conditional Expressions Functions -------------// - -func COALESCE(value Expression, values ...Expression) Expression { - var allValues = []Expression{value} - allValues = append(allValues, values...) - return newFunc("COALESCE", allValues, nil) -} - -func NULLIF(value1, value2 Expression) Expression { - return newFunc("NULLIF", []Expression{value1, value2}, nil) -} - -func GREATEST(value Expression, values ...Expression) Expression { - var allValues = []Expression{value} - allValues = append(allValues, values...) - return newFunc("GREATEST", allValues, nil) -} - -func LEAST(value Expression, values ...Expression) Expression { - var allValues = []Expression{value} - allValues = append(allValues, values...) - return newFunc("LEAST", allValues, nil) -} diff --git a/func_expression_test.go b/func_expression_test.go index d5d78e9..9b7f5b1 100644 --- a/func_expression_test.go +++ b/func_expression_test.go @@ -1,7 +1,6 @@ package jet import ( - "gotest.tools/assert" "testing" ) @@ -156,14 +155,3 @@ func TestFuncLEAST(t *testing.T) { assertClauseSerialize(t, LEAST(table1ColFloat), "LEAST(table1.col_float)") assertClauseSerialize(t, LEAST(Float(11.2222), NULL, String("str")), "LEAST($1, NULL, $2)", float64(11.2222), "str") } - -func TestInterval(t *testing.T) { - query := INTERVAL(`6 years 5 months 4 days 3 hours 2 minutes 1 second`) - - queryData := &sqlBuilder{} - - err := query.serialize(select_statement, queryData) - - assert.NilError(t, err) - assert.Equal(t, queryData.buff.String(), `INTERVAL $1`) -} diff --git a/insert_statement.go b/insert_statement.go index 1bb7d76..995ceb6 100644 --- a/insert_statement.go +++ b/insert_statement.go @@ -7,6 +7,7 @@ import ( "github.com/go-jet/jet/execution" ) +// InsertStatement is interface for SQL INSERT statements type InsertStatement interface { Statement @@ -85,7 +86,7 @@ func (i *insertStatementImpl) Sql() (sql string, args []interface{}, err error) return "", nil, errors.New("jet: table is nil") } - err = i.table.serialize(insert_statement, queryData) + err = i.table.serialize(insertStatement, queryData) if err != nil { return @@ -114,8 +115,8 @@ func (i *insertStatementImpl) Sql() (sql string, args []interface{}, err error) if len(i.rows) > 0 { queryData.writeString("VALUES") - for row_i, row := range i.rows { - if row_i > 0 { + for rowIndex, row := range i.rows { + if rowIndex > 0 { queryData.writeString(",") } @@ -123,7 +124,7 @@ func (i *insertStatementImpl) Sql() (sql string, args []interface{}, err error) queryData.newLine() queryData.writeString("(") - err = serializeClauseList(insert_statement, row, queryData) + err = serializeClauseList(insertStatement, row, queryData) if err != nil { return "", nil, err @@ -135,14 +136,14 @@ func (i *insertStatementImpl) Sql() (sql string, args []interface{}, err error) } if i.query != nil { - err = i.query.serialize(insert_statement, queryData) + err = i.query.serialize(insertStatement, queryData) if err != nil { return } } - if err = queryData.writeReturning(insert_statement, i.returning); err != nil { + if err = queryData.writeReturning(insertStatement, i.returning); err != nil { return } @@ -156,7 +157,7 @@ func (i *insertStatementImpl) Query(db execution.DB, destination interface{}) er } func (i *insertStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error { - return queryContext(i, db, context, destination) + return queryContext(context, i, db, destination) } func (i *insertStatementImpl) Exec(db execution.DB) (res sql.Result, err error) { diff --git a/insert_statement_test.go b/insert_statement_test.go index 71bb398..2c724f5 100644 --- a/insert_statement_test.go +++ b/insert_statement_test.go @@ -81,13 +81,13 @@ func TestInsertValuesFromModel(t *testing.T) { MODEL(toInsert). MODEL(&toInsert) - expectedSql := ` + expectedSQL := ` INSERT INTO db.table1 (col1, col_float) VALUES ($1, $2), ($3, $4); ` - assertStatement(t, stmt, expectedSql, int(1), float64(1.11), int(1), float64(1.11)) + assertStatement(t, stmt, expectedSQL, int(1), float64(1.11), int(1), float64(1.11)) } func TestInsertValuesFromModelColumnMismatch(t *testing.T) { @@ -125,23 +125,23 @@ func TestInsertQuery(t *testing.T) { stmt := table1.INSERT(table1Col1). QUERY(table1.SELECT(table1Col1)) - var expectedSql = ` + var expectedSQL = ` INSERT INTO db.table1 (col1) ( SELECT table1.col1 AS "table1.col1" FROM db.table1 ); ` - assertStatement(t, stmt, expectedSql) + assertStatement(t, stmt, expectedSQL) } func TestInsertDefaultValue(t *testing.T) { stmt := table1.INSERT(table1Col1, table1ColFloat). VALUES(DEFAULT, "two") - var expectedSql = ` + var expectedSQL = ` INSERT INTO db.table1 (col1, col_float) VALUES (DEFAULT, $1); ` - assertStatement(t, stmt, expectedSql, "two") + assertStatement(t, stmt, expectedSQL, "two") } diff --git a/integer_expression.go b/integer_expression.go index 10057f6..6d90645 100644 --- a/integer_expression.go +++ b/integer_expression.go @@ -1,5 +1,6 @@ package jet +// IntegerExpression interface type IntegerExpression interface { Expression numericExpression @@ -180,6 +181,9 @@ func newIntExpressionWrap(expression Expression) IntegerExpression { return &intExpressionWrap } +// IntExp is int expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as int expression. +// Does not add sql cast to generated sql builder output. func IntExp(expression Expression) IntegerExpression { return newIntExpressionWrap(expression) } diff --git a/keyword.go b/keyword.go index 6f363cf..988ae0a 100644 --- a/keyword.go +++ b/keyword.go @@ -1,11 +1,14 @@ package jet const ( + // DEFAULT is jet equivalent of SQL DEFAULT DEFAULT keywordClause = "DEFAULT" ) var ( + // NULL is jet equivalent of SQL NULL NULL = newNullLiteral() + // STAR is jet equivalent of SQL * STAR = newStarLiteral() ) diff --git a/literal_expression.go b/literal_expression.go index 9e8becf..27dca03 100644 --- a/literal_expression.go +++ b/literal_expression.go @@ -27,7 +27,7 @@ func (l literalExpression) serialize(statement statementType, out *sqlBuilder, o if l.constant { out.insertConstantArgument(l.value) } else { - out.insertPreparedArgument(l.value) + out.insertParametrizedArgument(l.value) } return nil @@ -38,6 +38,7 @@ type integerLiteralExpression struct { integerInterfaceImpl } +// Int is constructor for integer expressions literals. func Int(value int64) IntegerExpression { numLiteral := &integerLiteralExpression{} @@ -55,6 +56,7 @@ type boolLiteralExpression struct { literalExpression } +// Bool creates new bool literal expression func Bool(value bool) BoolExpression { boolLiteralExpression := boolLiteralExpression{} @@ -70,6 +72,7 @@ type floatLiteral struct { literalExpression } +// Float creates new float literal expression func Float(value float64) FloatExpression { floatLiteral := floatLiteral{} floatLiteral.literalExpression = *literal(value) @@ -85,6 +88,7 @@ type stringLiteral struct { literalExpression } +// String creates new string literal expression func String(value string) StringExpression { stringLiteral := stringLiteral{} stringLiteral.literalExpression = *literal(value) @@ -100,6 +104,7 @@ type timeLiteral struct { literalExpression } +// Time creates new time literal expression func Time(hour, minute, second, milliseconds int) TimeExpression { timeLiteral := &timeLiteral{} timeStr := fmt.Sprintf("%02d:%02d:%02d.%03d", hour, minute, second, milliseconds) @@ -116,6 +121,7 @@ type timezLiteral struct { literalExpression } +// Timez creates new time with time zone literal expression func Timez(hour, minute, second, milliseconds, timezone int) TimezExpression { timezLiteral := &timezLiteral{} timeStr := fmt.Sprintf("%02d:%02d:%02d.%03d %+03d", hour, minute, second, milliseconds, timezone) @@ -132,6 +138,7 @@ type timestampLiteral struct { literalExpression } +// Timestamp creates new timestamp literal expression func Timestamp(year, month, day, hour, minute, second, milliseconds int) TimestampExpression { timestampLiteral := ×tampLiteral{} timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, milliseconds) @@ -148,6 +155,7 @@ type timestampzLiteral struct { literalExpression } +// Timestampz creates new timestamp with time zone literal expression func Timestampz(year, month, day, hour, minute, second, milliseconds, timezone int) TimestampzExpression { timestampzLiteral := ×tampzLiteral{} timeStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d %+04d", @@ -166,6 +174,7 @@ type dateLiteral struct { literalExpression } +//Date creates new date expression func Date(year, month, day int) DateExpression { dateLiteral := &dateLiteral{} @@ -226,6 +235,7 @@ func (n *wrap) serialize(statement statementType, out *sqlBuilder, options ...se return err } +// WRAP wraps list of expressions with brackets '(' and ')' func WRAP(expression ...Expression) Expression { wrap := &wrap{expressions: expression} wrap.expressionInterfaceImpl.parent = wrap @@ -245,6 +255,8 @@ func (n *rawExpression) serialize(statement statementType, out *sqlBuilder, opti return nil } +// RAW can be used for any unsupported functions, operators or expressions. +// For example: RAW("current_database()") func RAW(raw string) Expression { rawExp := &rawExpression{raw: raw} rawExp.expressionInterfaceImpl.parent = rawExp diff --git a/lock_statement.go b/lock_statement.go index 40aca9d..26786da 100644 --- a/lock_statement.go +++ b/lock_statement.go @@ -7,8 +7,10 @@ import ( "github.com/go-jet/jet/execution" ) +// TableLockMode is a type of possible SQL table lock type TableLockMode string +// Lock types for LockStatement. const ( LOCK_ACCESS_SHARE = "ACCESS SHARE" LOCK_ROW_SHARE = "ROW SHARE" @@ -20,6 +22,7 @@ const ( LOCK_ACCESS_EXCLUSIVE = "ACCESS EXCLUSIVE" ) +// LockStatement interface for SQL LOCK statement type LockStatement interface { Statement @@ -33,6 +36,7 @@ type lockStatementImpl struct { nowait bool } +// LOCK creates lock statement for list of tables. func LOCK(tables ...WritableTable) LockStatement { return &lockStatementImpl{ tables: tables, @@ -55,11 +59,11 @@ func (l *lockStatementImpl) DebugSql() (query string, err error) { func (l *lockStatementImpl) Sql() (query string, args []interface{}, err error) { if l == nil { - return "", nil, errors.New("jet: nil Statement.") + return "", nil, errors.New("jet: nil Statement") } if len(l.tables) == 0 { - return "", nil, errors.New("jet: There is no table selected to be locked. ") + return "", nil, errors.New("jet: There is no table selected to be locked") } out := &sqlBuilder{} @@ -72,7 +76,7 @@ func (l *lockStatementImpl) Sql() (query string, args []interface{}, err error) out.writeString(", ") } - err := table.serialize(lock_statement, out) + err := table.serialize(lockStatement, out) if err != nil { return "", nil, err @@ -98,7 +102,7 @@ func (l *lockStatementImpl) Query(db execution.DB, destination interface{}) erro } func (l *lockStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error { - return queryContext(l, db, context, destination) + return queryContext(context, l, db, destination) } func (l *lockStatementImpl) Exec(db execution.DB) (sql.Result, error) { diff --git a/numeric_expression.go b/numeric_expression.go index 004fd40..8f34fc2 100644 --- a/numeric_expression.go +++ b/numeric_expression.go @@ -1,5 +1,6 @@ package jet +// NumericExpression is common interface for all integer and float expressions type NumericExpression interface { Expression numericExpression diff --git a/operators.go b/operators.go index fd12932..9e5187f 100644 --- a/operators.go +++ b/operators.go @@ -4,17 +4,19 @@ import "errors" //----------- Logical operators ---------------// -// Returns negation of bool expression expr +// NOT returns negation of bool expression result func NOT(exp BoolExpression) BoolExpression { return newPrefixBoolOperator(exp, "NOT") } +// BIT_NOT inverts every bit in integer expression result func BIT_NOT(expr IntegerExpression) IntegerExpression { return newPrefixIntegerOperator(expr, "~") } //----------- Comparison operators ---------------// +// EXISTS checks for existence of the rows in subQuery func EXISTS(subQuery SelectStatement) BoolExpression { return newPrefixBoolOperator(subQuery, "EXISTS") } @@ -59,12 +61,13 @@ func gtEq(lhs, rhs Expression) BoolExpression { // --------------- CASE operator -------------------// -type CaseOperatorExpression interface { +// CaseOperator is interface for SQL case operator +type CaseOperator interface { Expression - WHEN(condition Expression) CaseOperatorExpression - THEN(then Expression) CaseOperatorExpression - ELSE(els Expression) CaseOperatorExpression + WHEN(condition Expression) CaseOperator + THEN(then Expression) CaseOperator + ELSE(els Expression) CaseOperator } type caseOperatorImpl struct { @@ -76,7 +79,8 @@ type caseOperatorImpl struct { els Expression } -func CASE(expression ...Expression) CaseOperatorExpression { +// CASE create CASE operator with optional list of expressions +func CASE(expression ...Expression) CaseOperator { caseExp := &caseOperatorImpl{} if len(expression) > 0 { @@ -88,17 +92,17 @@ func CASE(expression ...Expression) CaseOperatorExpression { return caseExp } -func (c *caseOperatorImpl) WHEN(when Expression) CaseOperatorExpression { +func (c *caseOperatorImpl) WHEN(when Expression) CaseOperator { c.when = append(c.when, when) return c } -func (c *caseOperatorImpl) THEN(then Expression) CaseOperatorExpression { +func (c *caseOperatorImpl) THEN(then Expression) CaseOperator { c.then = append(c.then, then) return c } -func (c *caseOperatorImpl) ELSE(els Expression) CaseOperatorExpression { +func (c *caseOperatorImpl) ELSE(els Expression) CaseOperator { c.els = els return c diff --git a/operators_test.go b/operators_test.go index 6e6af04..84fc65b 100644 --- a/operators_test.go +++ b/operators_test.go @@ -5,6 +5,7 @@ import "testing" func TestOperatorNOT(t *testing.T) { notExpression := NOT(Int(2).EQ(Int(1))) + assertClauseSerialize(t, NOT(table1ColBool), "NOT table1.col_bool") assertClauseSerialize(t, notExpression, "NOT ($1 = $2)", int64(2), int64(1)) assertProjectionSerialize(t, notExpression.AS("alias_not_expression"), `NOT ($1 = $2) AS "alias_not_expression"`, int64(2), int64(1)) assertClauseSerialize(t, notExpression.AND(Int(4).EQ(Int(5))), `(NOT ($1 = $2) AND ($3 = $4))`, int64(2), int64(1), int64(4), int64(5)) diff --git a/order_by_clause.go b/order_by_clause.go index 51d9b3b..b09d8c9 100644 --- a/order_by_clause.go +++ b/order_by_clause.go @@ -2,7 +2,8 @@ package jet import "errors" -type OrderByClause interface { +// OrderByClause +type orderByClause interface { serializeForOrderBy(statement statementType, out *sqlBuilder) error } @@ -13,7 +14,7 @@ type orderByClauseImpl struct { func (o *orderByClauseImpl) serializeForOrderBy(statement statementType, out *sqlBuilder) error { if o.expression == nil { - return errors.New("jet: nil orderBy by clause.") + return errors.New("jet: nil orderBy by clause") } if err := o.expression.serializeForOrderBy(statement, out); err != nil { @@ -29,6 +30,6 @@ func (o *orderByClauseImpl) serializeForOrderBy(statement statementType, out *sq return nil } -func newOrderByClause(expression Expression, ascent bool) OrderByClause { +func newOrderByClause(expression Expression, ascent bool) orderByClause { return &orderByClauseImpl{expression: expression, ascent: ascent} } diff --git a/projection.go b/projection.go index 361e26f..f81922b 100644 --- a/projection.go +++ b/projection.go @@ -2,12 +2,13 @@ package jet type projection interface { serializeForProjection(statement statementType, out *sqlBuilder) error - from(subQuery ExpressionTable) projection + from(subQuery SelectTable) projection } +// ProjectionList is a redefined type, so that ProjectionList can be used as a projection. type ProjectionList []projection -func (cl ProjectionList) from(subQuery ExpressionTable) projection { +func (cl ProjectionList) from(subQuery SelectTable) projection { newProjectionList := ProjectionList{} for _, projection := range cl { diff --git a/select_statement.go b/select_statement.go index cd39bdd..47d79f5 100644 --- a/select_statement.go +++ b/select_statement.go @@ -7,6 +7,7 @@ import ( "github.com/go-jet/jet/execution" ) +// Select statements lock types var ( UPDATE = newLock("UPDATE") NO_KEY_UPDATE = newLock("NO KEY UPDATE") @@ -14,6 +15,7 @@ var ( KEY_SHARE = newLock("KEY SHARE") ) +// SelectStatement is interface for SQL SELECT statements type SelectStatement interface { Statement Expression @@ -23,7 +25,7 @@ type SelectStatement interface { WHERE(expression BoolExpression) SelectStatement GROUP_BY(groupByClauses ...groupByClause) SelectStatement HAVING(boolExpression BoolExpression) SelectStatement - ORDER_BY(orderByClauses ...OrderByClause) SelectStatement + ORDER_BY(orderByClauses ...orderByClause) SelectStatement LIMIT(limit int64) SelectStatement OFFSET(offset int64) SelectStatement FOR(lock SelectLock) SelectStatement @@ -35,11 +37,12 @@ type SelectStatement interface { EXCEPT(rhs SelectStatement) SelectStatement EXCEPT_ALL(rhs SelectStatement) SelectStatement - AsTable(alias string) ExpressionTable + AsTable(alias string) SelectTable projections() []projection } +//SELECT creates new SelectStatement with list of projections func SELECT(projection1 projection, projections ...projection) SelectStatement { return newSelectStatement(nil, append([]projection{projection1}, projections...)) } @@ -55,7 +58,7 @@ type selectStatementImpl struct { groupBy []groupByClause having BoolExpression - orderBy []OrderByClause + orderBy []orderByClause limit, offset int64 lockFor SelectLock @@ -81,8 +84,8 @@ func (s *selectStatementImpl) FROM(table ReadableTable) SelectStatement { return s.parent } -func (s *selectStatementImpl) AsTable(alias string) ExpressionTable { - return newExpressionTable(s.parent, alias, s.parent.projections()) +func (s *selectStatementImpl) AsTable(alias string) SelectTable { + return newSelectTable(s.parent, alias) } func (s *selectStatementImpl) WHERE(expression BoolExpression) SelectStatement { @@ -100,7 +103,7 @@ func (s *selectStatementImpl) HAVING(expression BoolExpression) SelectStatement return s.parent } -func (s *selectStatementImpl) ORDER_BY(clauses ...OrderByClause) SelectStatement { +func (s *selectStatementImpl) ORDER_BY(clauses ...orderByClause) SelectStatement { s.orderBy = clauses return s.parent } @@ -189,20 +192,20 @@ func (s *selectStatementImpl) serializeImpl(out *sqlBuilder) error { return errors.New("jet: no column selected for projection") } - err := out.writeProjections(select_statement, s.projectionList) + err := out.writeProjections(selectStatement, s.projectionList) if err != nil { return err } if s.table != nil { - if err := out.writeFrom(select_statement, s.table); err != nil { + if err := out.writeFrom(selectStatement, s.table); err != nil { return err } } if s.where != nil { - err := out.writeWhere(select_statement, s.where) + err := out.writeWhere(selectStatement, s.where) if err != nil { return nil @@ -210,7 +213,7 @@ func (s *selectStatementImpl) serializeImpl(out *sqlBuilder) error { } if s.groupBy != nil && len(s.groupBy) > 0 { - err := out.writeGroupBy(select_statement, s.groupBy) + err := out.writeGroupBy(selectStatement, s.groupBy) if err != nil { return err @@ -218,7 +221,7 @@ func (s *selectStatementImpl) serializeImpl(out *sqlBuilder) error { } if s.having != nil { - err := out.writeHaving(select_statement, s.having) + err := out.writeHaving(selectStatement, s.having) if err != nil { return err @@ -226,7 +229,7 @@ func (s *selectStatementImpl) serializeImpl(out *sqlBuilder) error { } if s.orderBy != nil { - err := out.writeOrderBy(select_statement, s.orderBy) + err := out.writeOrderBy(selectStatement, s.orderBy) if err != nil { return err @@ -236,19 +239,19 @@ func (s *selectStatementImpl) serializeImpl(out *sqlBuilder) error { if s.limit >= 0 { out.newLine() out.writeString("LIMIT") - out.insertPreparedArgument(s.limit) + out.insertParametrizedArgument(s.limit) } if s.offset >= 0 { out.newLine() out.writeString("OFFSET") - out.insertPreparedArgument(s.offset) + out.insertParametrizedArgument(s.offset) } if s.lockFor != nil { out.newLine() out.writeString("FOR") - err := s.lockFor.serialize(select_statement, out) + err := s.lockFor.serialize(selectStatement, out) if err != nil { return err @@ -281,7 +284,7 @@ func (s *selectStatementImpl) Query(db execution.DB, destination interface{}) er } func (s *selectStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error { - return queryContext(s.parent, db, context, destination) + return queryContext(context, s.parent, db, destination) } func (s *selectStatementImpl) Exec(db execution.DB) (res sql.Result, err error) { @@ -292,8 +295,7 @@ func (s *selectStatementImpl) ExecContext(db execution.DB, context context.Conte return execContext(s.parent, db, context) } -// SelectLock - +// SelectLock is interface for SELECT statement locks type SelectLock interface { clause diff --git a/select_table.go b/select_table.go new file mode 100644 index 0000000..2d7c901 --- /dev/null +++ b/select_table.go @@ -0,0 +1,63 @@ +package jet + +import "errors" + +// SelectTable is interface for SELECT sub-queries +type SelectTable interface { + ReadableTable + + Alias() string + + AllColumns() ProjectionList +} + +type selectTableImpl struct { + readableTableInterfaceImpl + selectStmt SelectStatement + alias string + + projections []projection +} + +func newSelectTable(selectStmt SelectStatement, alias string) SelectTable { + expTable := &selectTableImpl{selectStmt: selectStmt, alias: alias} + + expTable.readableTableInterfaceImpl.parent = expTable + + for _, projection := range selectStmt.projections() { + newProjection := projection.from(expTable) + + expTable.projections = append(expTable.projections, newProjection) + } + + return expTable +} + +func (s *selectTableImpl) Alias() string { + return s.alias +} + +func (s *selectTableImpl) columns() []column { + return nil +} + +func (s *selectTableImpl) AllColumns() ProjectionList { + return s.projections +} + +func (s *selectTableImpl) serialize(statement statementType, out *sqlBuilder, options ...serializeOption) error { + if s == nil { + return errors.New("jet: Expression table is nil. ") + } + + err := s.selectStmt.serialize(statement, out) + + if err != nil { + return err + } + + out.writeString("AS") + out.writeIdentifier(s.alias) + + return nil +} diff --git a/set_statement.go b/set_statement.go index 20c9bf4..54e5e77 100644 --- a/set_statement.go +++ b/set_statement.go @@ -4,28 +4,40 @@ import ( "errors" ) +// UNION effectively appends the result of sub-queries(select statements) into single query. +// It eliminates duplicate rows from its result. func UNION(lhs, rhs SelectStatement, selects ...SelectStatement) SelectStatement { return newSetStatementImpl(union, false, toSelectList(lhs, rhs, selects...)) } +// UNION_ALL effectively appends the result of sub-queries(select statements) into single query. +// It does not eliminates duplicate rows from its result. func UNION_ALL(lhs, rhs SelectStatement, selects ...SelectStatement) SelectStatement { return newSetStatementImpl(union, true, toSelectList(lhs, rhs, selects...)) } +// INTERSECT returns all rows that are in query results. +// It eliminates duplicate rows from its result. func INTERSECT(lhs, rhs SelectStatement, selects ...SelectStatement) SelectStatement { return newSetStatementImpl(intersect, false, toSelectList(lhs, rhs, selects...)) } +// INTERSECT_ALL returns all rows that are in query results. +// It does not eliminates duplicate rows from its result. func INTERSECT_ALL(lhs, rhs SelectStatement, selects ...SelectStatement) SelectStatement { return newSetStatementImpl(intersect, true, toSelectList(lhs, rhs, selects...)) } -func EXCEPT(lhs, rhs SelectStatement, selects ...SelectStatement) SelectStatement { - return newSetStatementImpl(except, false, toSelectList(lhs, rhs, selects...)) +// EXCEPT returns all rows that are in the result of query lhs but not in the result of query rhs. +// It eliminates duplicate rows from its result. +func EXCEPT(lhs, rhs SelectStatement) SelectStatement { + return newSetStatementImpl(except, false, toSelectList(lhs, rhs)) } -func EXCEPT_ALL(lhs, rhs SelectStatement, selects ...SelectStatement) SelectStatement { - return newSetStatementImpl(except, true, toSelectList(lhs, rhs, selects...)) +// EXCEPT_ALL returns all rows that are in the result of query lhs but not in the result of query rhs. +// It does not eliminates duplicate rows from its result. +func EXCEPT_ALL(lhs, rhs SelectStatement) SelectStatement { + return newSetStatementImpl(except, true, toSelectList(lhs, rhs)) } func toSelectList(lhs, rhs SelectStatement, selects ...SelectStatement) []SelectStatement { @@ -102,7 +114,7 @@ func (s *setStatementImpl) serializeImpl(out *sqlBuilder) error { } if len(s.selects) < 2 { - return errors.New("jet: UNION Statement must have at least two SELECT statements.") + return errors.New("jet: UNION Statement must have at least two SELECT statements") } out.newLine() @@ -124,7 +136,7 @@ func (s *setStatementImpl) serializeImpl(out *sqlBuilder) error { return errors.New("jet: select statement is nil") } - err := selectStmt.serialize(set_statement, out) + err := selectStmt.serialize(setStatement, out) if err != nil { return err @@ -136,7 +148,7 @@ func (s *setStatementImpl) serializeImpl(out *sqlBuilder) error { out.writeString(")") if s.orderBy != nil { - err := out.writeOrderBy(set_statement, s.orderBy) + err := out.writeOrderBy(setStatement, s.orderBy) if err != nil { return err } @@ -145,13 +157,13 @@ func (s *setStatementImpl) serializeImpl(out *sqlBuilder) error { if s.limit >= 0 { out.newLine() out.writeString("LIMIT") - out.insertPreparedArgument(s.limit) + out.insertParametrizedArgument(s.limit) } if s.offset >= 0 { out.newLine() out.writeString("OFFSET") - out.insertPreparedArgument(s.offset) + out.insertParametrizedArgument(s.offset) } return nil diff --git a/set_statement_test.go b/set_statement_test.go index cd2ebe6..159fb0b 100644 --- a/set_statement_test.go +++ b/set_statement_test.go @@ -6,7 +6,7 @@ import ( ) func TestUnionTwoSelect(t *testing.T) { - var expectedSql = ` + var expectedSQL = ` ( ( SELECT table1.col1 AS "table1.col1" @@ -27,8 +27,8 @@ func TestUnionTwoSelect(t *testing.T) { unionStmt2 := UNION(table1.SELECT(table1Col1), table2.SELECT(table2Col3)) - assertStatement(t, unionStmt1, expectedSql) - assertStatement(t, unionStmt2, expectedSql) + assertStatement(t, unionStmt1, expectedSQL) + assertStatement(t, unionStmt2, expectedSQL) } func TestUnionNilSelect(t *testing.T) { @@ -49,7 +49,7 @@ func TestUnionThreeSelect1(t *testing.T) { table3.SELECT(table3Col1), ) - var expectedSql = ` + var expectedSQL = ` ( ( @@ -71,7 +71,7 @@ func TestUnionThreeSelect1(t *testing.T) { ); ` - assertStatement(t, unionStmt1, expectedSql) + assertStatement(t, unionStmt1, expectedSQL) } func TestUnionThreeSelect2(t *testing.T) { @@ -82,7 +82,7 @@ func TestUnionThreeSelect2(t *testing.T) { table3.SELECT(table3Col1), ) - var expectedSql = ` + var expectedSQL = ` ( ( SELECT table1.col1 AS "table1.col1" @@ -101,7 +101,7 @@ func TestUnionThreeSelect2(t *testing.T) { ); ` - assertStatement(t, unionStmt2, expectedSql) + assertStatement(t, unionStmt2, expectedSQL) } func TestUnionWithOrderBy(t *testing.T) { @@ -155,7 +155,7 @@ OFFSET $2; } func TestUnionInUnion(t *testing.T) { - expectedSql := ` + expectedSQL := ` ( ( SELECT table2.col3 AS "table2.col3", @@ -182,7 +182,7 @@ func TestUnionInUnion(t *testing.T) { UNION_ALL(table1.SELECT(table1Col1), table2.SELECT(table2Col3)), ) - assertStatement(t, query, expectedSql) + assertStatement(t, query, expectedSQL) } func TestUnionALL(t *testing.T) { diff --git a/statement.go b/statement.go index a681525..2ed4fa0 100644 --- a/statement.go +++ b/statement.go @@ -8,6 +8,7 @@ import ( "strings" ) +//Statement is common interface for all statements(SELECT, INSERT, UPDATE, DELETE, LOCK) type Statement interface { // Sql returns parametrized sql query with list of arguments. // err is returned if statement is not composed correctly @@ -37,14 +38,14 @@ func debugSql(statement Statement) (string, error) { return "", err } - debugSqlQuery := sqlQuery + debugSQLQuery := sqlQuery for i, arg := range args { argPlaceholder := "$" + strconv.Itoa(i+1) - debugSqlQuery = strings.Replace(debugSqlQuery, argPlaceholder, ArgToString(arg), 1) + debugSQLQuery = strings.Replace(debugSQLQuery, argPlaceholder, argToString(arg), 1) } - return debugSqlQuery, nil + return debugSQLQuery, nil } func query(statement Statement, db execution.DB, destination interface{}) error { @@ -57,7 +58,7 @@ func query(statement Statement, db execution.DB, destination interface{}) error return execution.Query(db, context.Background(), query, args, destination) } -func queryContext(statement Statement, db execution.DB, context context.Context, destination interface{}) error { +func queryContext(context context.Context, statement Statement, db execution.DB, destination interface{}) error { query, args, err := statement.Sql() if err != nil { diff --git a/string_expression.go b/string_expression.go index 7559b98..4e5d5d3 100644 --- a/string_expression.go +++ b/string_expression.go @@ -1,5 +1,6 @@ package jet +// StringExpression interface type StringExpression interface { Expression @@ -108,6 +109,9 @@ func newStringExpressionWrap(expression Expression) StringExpression { return &stringExpressionWrap } +// StringExp is string expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as string expression. +// Does not add sql cast to generated sql builder output. func StringExp(expression Expression) StringExpression { return newStringExpressionWrap(expression) } diff --git a/table.go b/table.go index 6299f1d..e4f5657 100644 --- a/table.go +++ b/table.go @@ -36,18 +36,21 @@ type writableTable interface { LOCK() LockStatement } +// ReadableTable interface type ReadableTable interface { table readableTable clause } +// WritableTable interface type WritableTable interface { table writableTable clause } +// Table interface type Table interface { table readableTable @@ -110,6 +113,7 @@ func (w *writableTableInterfaceImpl) LOCK() LockStatement { return LOCK(w.parent) } +// NewTable creates new table with schema name, table name and list of columns func NewTable(schemaName, name string, columns ...Column) Table { t := &tableImpl{ @@ -196,20 +200,20 @@ type joinTable struct { lhs ReadableTable rhs ReadableTable - join_type joinType + joinType joinType onCondition BoolExpression } func newJoinTable( lhs ReadableTable, rhs ReadableTable, - join_type joinType, + joinType joinType, onCondition BoolExpression) ReadableTable { joinTable := &joinTable{ lhs: lhs, rhs: rhs, - join_type: join_type, + joinType: joinType, onCondition: onCondition, } @@ -245,7 +249,7 @@ func (t *joinTable) serialize(statement statementType, out *sqlBuilder, options out.newLine() - switch t.join_type { + switch t.joinType { case innerJoin: out.writeString("INNER JOIN") case leftJoin: @@ -266,7 +270,7 @@ func (t *joinTable) serialize(statement statementType, out *sqlBuilder, options return } - if t.onCondition == nil && t.join_type != crossJoin { + if t.onCondition == nil && t.joinType != crossJoin { return errors.New("jet: join condition is nil") } diff --git a/tests/all_types_test.go b/tests/all_types_test.go index ae5c14b..23111fc 100644 --- a/tests/all_types_test.go +++ b/tests/all_types_test.go @@ -229,7 +229,7 @@ func TestFloatOperators(t *testing.T) { CEIL(AllTypes.Real), FLOOR(AllTypes.Real), ROUND(AllTypes.Decimal), - ROUND(AllTypes.Decimal, Int(3)).AS("round"), + ROUND(AllTypes.Decimal, AllTypes.Integer).AS("round"), SIGN(AllTypes.Real), TRUNC(AllTypes.Decimal), TRUNC(AllTypes.Decimal, Int(1)), @@ -361,7 +361,7 @@ func TestSubQueryColumnReference(t *testing.T) { args []interface{} } - subQueries := map[ExpressionTable]expected{} + subQueries := map[SelectTable]expected{} selectSubQuery := AllTypes.SELECT( AllTypes.Boolean, @@ -378,7 +378,7 @@ func TestSubQueryColumnReference(t *testing.T) { LIMIT(2). AsTable("subQuery") - var selectExpectedSql = ` ( + var selectexpectedSQL = ` ( SELECT all_types.boolean AS "all_types.boolean", all_types.integer AS "all_types.integer", all_types.real AS "all_types.real", @@ -424,7 +424,7 @@ func TestSubQueryColumnReference(t *testing.T) { ). AsTable("subQuery") - unionExpectedSql := ` + unionexpectedSQL := ` ( ( SELECT all_types.boolean AS "all_types.boolean", @@ -458,8 +458,8 @@ func TestSubQueryColumnReference(t *testing.T) { ) ) AS "subQuery"` - subQueries[selectSubQuery] = expected{sql: selectExpectedSql, args: []interface{}{int64(2)}} - subQueries[unionSubQuery] = expected{sql: unionExpectedSql, args: []interface{}{int64(1), int64(1), int64(1)}} + subQueries[selectSubQuery] = expected{sql: selectexpectedSQL, args: []interface{}{int64(2)}} + subQueries[unionSubQuery] = expected{sql: unionexpectedSQL, args: []interface{}{int64(1), int64(1), int64(1)}} for subQuery, expected := range subQueries { boolColumn := AllTypes.Boolean.From(subQuery) @@ -487,7 +487,7 @@ func TestSubQueryColumnReference(t *testing.T) { ). FROM(subQuery) - var expectedSql = ` + var expectedSQL = ` SELECT "subQuery"."all_types.boolean" AS "all_types.boolean", "subQuery"."all_types.integer" AS "all_types.integer", "subQuery"."all_types.real" AS "all_types.real", @@ -500,7 +500,7 @@ SELECT "subQuery"."all_types.boolean" AS "all_types.boolean", "subQuery"."aliasedColumn" AS "aliasedColumn" FROM` - assertStatementSql(t, stmt1, expectedSql+expected.sql+";\n", expected.args...) + assertStatementSql(t, stmt1, expectedSQL+expected.sql+";\n", expected.args...) dest1 := []model.AllTypes{} err := stmt1.Query(db, &dest1) @@ -523,7 +523,7 @@ FROM` //fmt.Println(stmt2.DebugSql()) - assertStatementSql(t, stmt2, expectedSql+expected.sql+";\n", expected.args...) + assertStatementSql(t, stmt2, expectedSQL+expected.sql+";\n", expected.args...) dest2 := []model.AllTypes{} err = stmt2.Query(db, &dest2) diff --git a/tests/delete_test.go b/tests/delete_test.go index 3cbe1e4..6f87161 100644 --- a/tests/delete_test.go +++ b/tests/delete_test.go @@ -11,7 +11,7 @@ import ( func TestDeleteWithWhere(t *testing.T) { initForDeleteTest(t) - var expectedSql = ` + var expectedSQL = ` DELETE FROM test_sample.link WHERE link.name IN ('Gmail', 'Outlook'); ` @@ -19,14 +19,14 @@ WHERE link.name IN ('Gmail', 'Outlook'); DELETE(). WHERE(Link.Name.IN(String("Gmail"), String("Outlook"))) - assertStatementSql(t, deleteStmt, expectedSql, "Gmail", "Outlook") + assertStatementSql(t, deleteStmt, expectedSQL, "Gmail", "Outlook") assertExec(t, deleteStmt, 2) } func TestDeleteWithWhereAndReturning(t *testing.T) { initForDeleteTest(t) - var expectedSql = ` + var expectedSQL = ` DELETE FROM test_sample.link WHERE link.name IN ('Gmail', 'Outlook') RETURNING link.id AS "link.id", @@ -39,7 +39,7 @@ RETURNING link.id AS "link.id", WHERE(Link.Name.IN(String("Gmail"), String("Outlook"))). RETURNING(Link.AllColumns) - assertStatementSql(t, deleteStmt, expectedSql, "Gmail", "Outlook") + assertStatementSql(t, deleteStmt, expectedSQL, "Gmail", "Outlook") dest := []model.Link{} diff --git a/tests/insert_test.go b/tests/insert_test.go index c636c85..35fd8ab 100644 --- a/tests/insert_test.go +++ b/tests/insert_test.go @@ -11,7 +11,7 @@ import ( func TestInsertValues(t *testing.T) { cleanUpLinkTable(t) - var expectedSql = ` + var expectedSQL = ` INSERT INTO test_sample.link (id, url, name, description) VALUES (100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT), (101, 'http://www.google.com', 'Google', DEFAULT), @@ -28,7 +28,7 @@ RETURNING link.id AS "link.id", VALUES(102, "http://www.yahoo.com", "Yahoo", nil). RETURNING(Link.AllColumns) - assertStatementSql(t, insertQuery, expectedSql, + assertStatementSql(t, insertQuery, expectedSQL, 100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", 101, "http://www.google.com", "Google", 102, "http://www.yahoo.com", "Yahoo", nil) @@ -74,7 +74,7 @@ RETURNING link.id AS "link.id", func TestInsertEmptyColumnList(t *testing.T) { cleanUpLinkTable(t) - expectedSql := ` + expectedSQL := ` INSERT INTO test_sample.link VALUES (100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT); ` @@ -82,7 +82,7 @@ INSERT INTO test_sample.link VALUES stmt := Link.INSERT(). VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT) - assertStatementSql(t, stmt, expectedSql, + assertStatementSql(t, stmt, expectedSQL, 100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial") assertExec(t, stmt, 1) @@ -90,7 +90,7 @@ INSERT INTO test_sample.link VALUES func TestInsertModelObject(t *testing.T) { cleanUpLinkTable(t) - var expectedSql = ` + var expectedSQL = ` INSERT INTO test_sample.link (url, name) VALUES ('http://www.duckduckgo.com', 'Duck Duck go'); ` @@ -104,7 +104,7 @@ INSERT INTO test_sample.link (url, name) VALUES INSERT(Link.URL, Link.Name). MODEL(linkData) - assertStatementSql(t, query, expectedSql, "http://www.duckduckgo.com", "Duck Duck go") + assertStatementSql(t, query, expectedSQL, "http://www.duckduckgo.com", "Duck Duck go") result, err := query.Exec(db) @@ -116,7 +116,7 @@ INSERT INTO test_sample.link (url, name) VALUES } func TestInsertModelsObject(t *testing.T) { - expectedSql := ` + expectedSQL := ` INSERT INTO test_sample.link (url, name) VALUES ('http://www.postgresqltutorial.com', 'PostgreSQL Tutorial'), ('http://www.google.com', 'Google'), @@ -142,7 +142,7 @@ INSERT INTO test_sample.link (url, name) VALUES INSERT(Link.URL, Link.Name). MODELS([]model.Link{tutorial, google, yahoo}) - assertStatementSql(t, stmt, expectedSql, + assertStatementSql(t, stmt, expectedSQL, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", "http://www.google.com", "Google", "http://www.yahoo.com", "Yahoo") @@ -151,7 +151,7 @@ INSERT INTO test_sample.link (url, name) VALUES } func TestInsertUsingMutableColumns(t *testing.T) { - var expectedSql = ` + var expectedSQL = ` INSERT INTO test_sample.link (url, name, description) VALUES ('http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT), ('http://www.google.com', 'Google', NULL), @@ -175,7 +175,7 @@ INSERT INTO test_sample.link (url, name, description) VALUES MODEL(google). MODELS([]model.Link{google, yahoo}) - assertStatementSql(t, stmt, expectedSql, + assertStatementSql(t, stmt, expectedSQL, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", "http://www.google.com", "Google", nil, "http://www.google.com", "Google", nil, @@ -190,7 +190,7 @@ func TestInsertQuery(t *testing.T) { Exec(db) assert.NilError(t, err) - var expectedSql = ` + var expectedSQL = ` INSERT INTO test_sample.link (url, name) ( SELECT link.url AS "link.url", link.name AS "link.name" @@ -212,7 +212,7 @@ RETURNING link.id AS "link.id", ). RETURNING(Link.AllColumns) - assertStatementSql(t, query, expectedSql, int64(0)) + assertStatementSql(t, query, expectedSQL, int64(0)) dest := []model.Link{} diff --git a/tests/sample_test.go b/tests/sample_test.go index c4ccec8..f09b027 100644 --- a/tests/sample_test.go +++ b/tests/sample_test.go @@ -72,7 +72,7 @@ FROM test_sample.person; func TestSelecSelfJoin1(t *testing.T) { - var expectedSql = ` + var expectedSQL = ` SELECT employee.employee_id AS "employee.employee_id", employee.first_name AS "employee.first_name", employee.last_name AS "employee.last_name", @@ -97,7 +97,7 @@ ORDER BY employee.employee_id; ). ORDER_BY(Employee.EmployeeID) - assertStatementSql(t, query, expectedSql) + assertStatementSql(t, query, expectedSQL) type Manager model.Employee diff --git a/tests/select_test.go b/tests/select_test.go index b450405..e269970 100644 --- a/tests/select_test.go +++ b/tests/select_test.go @@ -10,7 +10,7 @@ import ( ) func TestSelect_ScanToStruct(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT DISTINCT actor.actor_id AS "actor.actor_id", actor.first_name AS "actor.first_name", actor.last_name AS "actor.last_name", @@ -24,7 +24,7 @@ WHERE actor.actor_id = 1; DISTINCT(). WHERE(Actor.ActorID.EQ(Int(1))) - assertStatementSql(t, query, expectedSql, int64(1)) + assertStatementSql(t, query, expectedSQL, int64(1)) actor := model.Actor{} err := query.Query(db, &actor) @@ -42,7 +42,7 @@ WHERE actor.actor_id = 1; } func TestClassicSelect(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT payment.payment_id AS "payment.payment_id", payment.customer_id AS "payment.customer_id", payment.staff_id AS "payment.staff_id", @@ -74,7 +74,7 @@ LIMIT 30; ORDER_BY(Payment.PaymentID.ASC()). LIMIT(30) - assertStatementSql(t, query, expectedSql, int64(30)) + assertStatementSql(t, query, expectedSQL, int64(30)) dest := []model.Payment{} @@ -85,7 +85,7 @@ LIMIT 30; } func TestSelect_ScanToSlice(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT customer.customer_id AS "customer.customer_id", customer.store_id AS "customer.store_id", customer.first_name AS "customer.first_name", @@ -103,7 +103,7 @@ ORDER BY customer.customer_id ASC; query := Customer.SELECT(Customer.AllColumns).ORDER_BY(Customer.CustomerID.ASC()) - assertStatementSql(t, query, expectedSql) + assertStatementSql(t, query, expectedSQL) err := query.Query(db, &customers) assert.NilError(t, err) @@ -116,7 +116,7 @@ ORDER BY customer.customer_id ASC; } func TestSelectAndUnionInProjection(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT payment.payment_id AS "payment.payment_id", ( SELECT customer.customer_id AS "customer.customer_id" @@ -156,12 +156,12 @@ LIMIT 12; ). LIMIT(12) - assertStatementSql(t, query, expectedSql, int64(1), int64(1), int64(10), int64(1), int64(2), int64(1), int64(12)) + assertStatementSql(t, query, expectedSQL, int64(1), int64(1), int64(10), int64(1), int64(2), int64(1), int64(12)) } func TestJoinQueryStruct(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT film_actor.actor_id AS "film_actor.actor_id", film_actor.film_id AS "film_actor.film_id", film_actor.last_update AS "film_actor.last_update", @@ -224,7 +224,7 @@ LIMIT 1000; ORDER_BY(Film.FilmID.ASC()). LIMIT(1000) - assertStatementSql(t, query, expectedSql, int64(1000)) + assertStatementSql(t, query, expectedSQL, int64(1000)) var languageActorFilm []struct { model.Language @@ -253,7 +253,7 @@ LIMIT 1000; } func TestJoinQuerySlice(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT language.language_id AS "language.language_id", language.name AS "language.name", language.last_update AS "language.last_update", @@ -290,7 +290,7 @@ LIMIT 15; WHERE(Film.Rating.EQ(enum.MpaaRating.Nc17)). LIMIT(15) - assertStatementSql(t, query, expectedSql, int64(15)) + assertStatementSql(t, query, expectedSQL, int64(15)) err := query.Query(db, &filmsPerLanguage) @@ -657,7 +657,7 @@ func TestSelectOrderByAscDesc(t *testing.T) { } func TestSelectFullJoin(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT customer.customer_id AS "customer.customer_id", customer.store_id AS "customer.store_id", customer.first_name AS "customer.first_name", @@ -685,7 +685,7 @@ ORDER BY customer.customer_id ASC; SELECT(Customer.AllColumns, Address.AllColumns). ORDER_BY(Customer.CustomerID.ASC()) - assertStatementSql(t, query, expectedSql) + assertStatementSql(t, query, expectedSQL) allCustomersAndAddress := []struct { Address *model.Address @@ -708,7 +708,7 @@ ORDER BY customer.customer_id ASC; } func TestSelectFullCrossJoin(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT customer.customer_id AS "customer.customer_id", customer.store_id AS "customer.store_id", customer.first_name AS "customer.first_name", @@ -738,7 +738,7 @@ LIMIT 1000; ORDER_BY(Customer.CustomerID.ASC()). LIMIT(1000) - assertStatementSql(t, query, expectedSql, int64(1000)) + assertStatementSql(t, query, expectedSQL, int64(1000)) var customerAddresCrosJoined []struct { model.Customer @@ -753,7 +753,7 @@ LIMIT 1000; } func TestSelectSelfJoin(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT f1.film_id AS "f1.film_id", f1.title AS "f1.title", f1.description AS "f1.description", @@ -793,7 +793,7 @@ ORDER BY f1.film_id ASC; SELECT(f1.AllColumns, f2.AllColumns). ORDER_BY(f1.FilmID.ASC()) - assertStatementSql(t, query, expectedSql) + assertStatementSql(t, query, expectedSQL) type F1 model.Film type F2 model.Film @@ -813,7 +813,7 @@ ORDER BY f1.film_id ASC; } func TestSelectAliasColumn(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT f1.title AS "thesame_length_films.title1", f2.title AS "thesame_length_films.title2", f1.length AS "thesame_length_films.length" @@ -835,7 +835,7 @@ LIMIT 1000; ORDER_BY(f1.Length.ASC(), f1.Title.ASC(), f2.Title.ASC()). LIMIT(1000) - assertStatementSql(t, query, expectedSql, int64(1000)) + assertStatementSql(t, query, expectedSQL, int64(1000)) type thesameLengthFilms struct { Title1 string @@ -928,7 +928,7 @@ FROM dvds.film; } func TestSelectQueryScalar(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT film.film_id AS "film.film_id", film.title AS "film.title", film.description AS "film.description", @@ -960,7 +960,7 @@ ORDER BY film.film_id ASC; WHERE(Film.RentalRate.EQ(maxFilmRentalRate)). ORDER_BY(Film.FilmID.ASC()) - assertStatementSql(t, query, expectedSql) + assertStatementSql(t, query, expectedSQL) maxRentalRateFilms := []model.Film{} err := query.Query(db, &maxRentalRateFilms) @@ -989,7 +989,7 @@ ORDER BY film.film_id ASC; } func TestSelectGroupByHaving(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT payment.customer_id AS "customer_payment_sum.customer_id", SUM(payment.amount) AS "customer_payment_sum.amount_sum", AVG(payment.amount) AS "customer_payment_sum.amount_avg", @@ -1018,7 +1018,7 @@ ORDER BY SUM(payment.amount) ASC; SUMf(Payment.Amount).GT(Float(100)), ) - assertStatementSql(t, customersPaymentQuery, expectedSql, float64(100)) + assertStatementSql(t, customersPaymentQuery, expectedSQL, float64(100)) type CustomerPaymentSum struct { CustomerID int16 @@ -1047,7 +1047,7 @@ ORDER BY SUM(payment.amount) ASC; } func TestSelectGroupBy2(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT customer.customer_id AS "customer.customer_id", customer.store_id AS "customer.store_id", customer.first_name AS "customer.first_name", @@ -1088,7 +1088,7 @@ ORDER BY customer_payment_sum."amount_sum" ASC; ). ORDER_BY(amountSum.ASC()) - assertStatementSql(t, query, expectedSql) + assertStatementSql(t, query, expectedSQL) type CustomerWithAmounts struct { Customer *model.Customer @@ -1157,7 +1157,7 @@ func TestSelectStaff(t *testing.T) { func TestSelectTimeColumns(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT payment.payment_id AS "payment.payment_id", payment.customer_id AS "payment.customer_id", payment.staff_id AS "payment.staff_id", @@ -1173,7 +1173,7 @@ ORDER BY payment.payment_date ASC; WHERE(Payment.PaymentDate.LT(Timestamp(2007, 02, 14, 22, 16, 01, 0))). ORDER_BY(Payment.PaymentDate.ASC()) - assertStatementSql(t, query, expectedSql, "2007-02-14 22:16:01.000") + assertStatementSql(t, query, expectedSQL, "2007-02-14 22:16:01.000") payments := []model.Payment{} @@ -1260,8 +1260,8 @@ func TestAllSetOperators(t *testing.T) { UNION_ALL, INTERSECT, INTERSECT_ALL, - EXCEPT, - EXCEPT_ALL, + //EXCEPT, + //EXCEPT_ALL, } expectedDestLen := []int{ @@ -1316,7 +1316,7 @@ LIMIT 20; } func TestLockTable(t *testing.T) { - expectedSql := ` + expectedSQL := ` LOCK TABLE dvds.address IN` var testData = []TableLockMode{ @@ -1333,7 +1333,7 @@ LOCK TABLE dvds.address IN` for _, lockMode := range testData { query := Address.LOCK().IN(lockMode) - assertStatementSql(t, query, expectedSql+" "+string(lockMode)+" MODE;\n") + assertStatementSql(t, query, expectedSQL+" "+string(lockMode)+" MODE;\n") tx, _ := db.Begin() @@ -1349,7 +1349,7 @@ LOCK TABLE dvds.address IN` for _, lockMode := range testData { query := Address.LOCK().IN(lockMode).NOWAIT() - assertStatementSql(t, query, expectedSql+" "+string(lockMode)+" MODE NOWAIT;\n") + assertStatementSql(t, query, expectedSQL+" "+string(lockMode)+" MODE NOWAIT;\n") tx, _ := db.Begin() @@ -1373,7 +1373,7 @@ func getRowLockTestData() map[SelectLock]string { } func TestRowLock(t *testing.T) { - expectedSql := ` + expectedSQL := ` SELECT * FROM dvds.address LIMIT 3 @@ -1385,7 +1385,7 @@ FOR` for lockType, lockTypeStr := range getRowLockTestData() { query.FOR(lockType) - assertStatementSql(t, query, expectedSql+" "+lockTypeStr+";\n", int64(3)) + assertStatementSql(t, query, expectedSQL+" "+lockTypeStr+";\n", int64(3)) tx, _ := db.Begin() @@ -1401,7 +1401,7 @@ FOR` for lockType, lockTypeStr := range getRowLockTestData() { query.FOR(lockType.NOWAIT()) - assertStatementSql(t, query, expectedSql+" "+lockTypeStr+" NOWAIT;\n", int64(3)) + assertStatementSql(t, query, expectedSQL+" "+lockTypeStr+" NOWAIT;\n", int64(3)) tx, _ := db.Begin() @@ -1417,7 +1417,7 @@ FOR` for lockType, lockTypeStr := range getRowLockTestData() { query.FOR(lockType.SKIP_LOCKED()) - assertStatementSql(t, query, expectedSql+" "+lockTypeStr+" SKIP LOCKED;\n", int64(3)) + assertStatementSql(t, query, expectedSQL+" "+lockTypeStr+" SKIP LOCKED;\n", int64(3)) tx, _ := db.Begin() @@ -1433,7 +1433,7 @@ FOR` func TestQuickStart(t *testing.T) { - var expectedSql = ` + var expectedSQL = ` SELECT actor.actor_id AS "actor.actor_id", actor.first_name AS "actor.first_name", actor.last_name AS "actor.last_name", @@ -1488,7 +1488,7 @@ ORDER BY actor.actor_id ASC, film.film_id ASC; Film.FilmID.ASC(), ) - assertStatementSql(t, stmt, expectedSql, "English", "Action", int64(180)) + assertStatementSql(t, stmt, expectedSQL, "English", "Action", int64(180)) var dest []struct { model.Actor diff --git a/tests/update_test.go b/tests/update_test.go index 5e10c7a..d8e1f0e 100644 --- a/tests/update_test.go +++ b/tests/update_test.go @@ -16,12 +16,12 @@ func TestUpdateValues(t *testing.T) { SET("Bong", "http://bong.com"). WHERE(Link.Name.EQ(String("Bing"))) - var expectedSql = ` + var expectedSQL = ` UPDATE test_sample.link SET (name, url) = ('Bong', 'http://bong.com') WHERE link.name = 'Bing'; ` - assertStatementSql(t, query, expectedSql, "Bong", "http://bong.com", "Bing") + assertStatementSql(t, query, expectedSQL, "Bong", "http://bong.com", "Bing") assertExec(t, query, 1) @@ -54,7 +54,7 @@ func TestUpdateWithSubQueries(t *testing.T) { ). WHERE(Link.Name.EQ(String("Bing"))) - expectedSql := ` + expectedSQL := ` UPDATE test_sample.link SET (name, url) = (( SELECT 'Bong' @@ -66,7 +66,7 @@ SET (name, url) = (( WHERE link.name = 'Bing'; ` - assertStatementSql(t, query, expectedSql, "Bong", "Bing", "Bing") + assertStatementSql(t, query, expectedSQL, "Bong", "Bing", "Bing") assertExec(t, query, 1) } @@ -74,7 +74,7 @@ WHERE link.name = 'Bing'; func TestUpdateAndReturning(t *testing.T) { setupLinkTableForUpdateTest(t) - expectedSql := ` + expectedSQL := ` UPDATE test_sample.link SET (name, url) = ('DuckDuckGo', 'http://www.duckduckgo.com') WHERE link.name = 'Ask' @@ -90,7 +90,7 @@ RETURNING link.id AS "link.id", WHERE(Link.Name.EQ(String("Ask"))). RETURNING(Link.AllColumns) - assertStatementSql(t, stmt, expectedSql, "DuckDuckGo", "http://www.duckduckgo.com", "Ask") + assertStatementSql(t, stmt, expectedSQL, "DuckDuckGo", "http://www.duckduckgo.com", "Ask") links := []model.Link{} @@ -112,7 +112,7 @@ func TestUpdateWithSelect(t *testing.T) { ). WHERE(Link.ID.EQ(Int(0))) - expectedSql := ` + expectedSQL := ` UPDATE test_sample.link SET (id, url, name, description) = ( SELECT link.id AS "link.id", @@ -124,7 +124,7 @@ SET (id, url, name, description) = ( ) WHERE link.id = 0; ` - assertStatementSql(t, stmt, expectedSql, int64(0), int64(0)) + assertStatementSql(t, stmt, expectedSQL, int64(0), int64(0)) assertExec(t, stmt, 1) } @@ -139,7 +139,7 @@ func TestUpdateWithInvalidSelect(t *testing.T) { ). WHERE(Link.ID.EQ(Int(0))) - var expectedSql = ` + var expectedSQL = ` UPDATE test_sample.link SET (id, url, name, description) = ( SELECT link.id AS "link.id", @@ -149,7 +149,7 @@ SET (id, url, name, description) = ( ) WHERE link.id = 0; ` - assertStatementSql(t, stmt, expectedSql, int64(0), int64(0)) + assertStatementSql(t, stmt, expectedSQL, int64(0), int64(0)) assertExecErr(t, stmt, "pq: number of columns does not match number of values") } @@ -168,12 +168,12 @@ func TestUpdateWithModelData(t *testing.T) { MODEL(link). WHERE(Link.ID.EQ(Int(int64(link.ID)))) - expectedSql := ` + expectedSQL := ` UPDATE test_sample.link SET (id, url, name, description) = (201, 'http://www.duckduckgo.com', 'DuckDuckGo', NULL) WHERE link.id = 201; ` - assertStatementSql(t, stmt, expectedSql, int32(201), "http://www.duckduckgo.com", "DuckDuckGo", nil, int64(201)) + assertStatementSql(t, stmt, expectedSQL, int32(201), "http://www.duckduckgo.com", "DuckDuckGo", nil, int64(201)) assertExec(t, stmt, 1) } @@ -195,12 +195,12 @@ func TestUpdateWithModelDataAndPredefinedColumnList(t *testing.T) { MODEL(link). WHERE(Link.ID.EQ(Int(int64(link.ID)))) - var expectedSql = ` + var expectedSQL = ` UPDATE test_sample.link SET (description, name, url) = (NULL, 'DuckDuckGo', 'http://www.duckduckgo.com') WHERE link.id = 201; ` - assertStatementSql(t, stmt, expectedSql, nil, "DuckDuckGo", "http://www.duckduckgo.com", int64(201)) + assertStatementSql(t, stmt, expectedSQL, nil, "DuckDuckGo", "http://www.duckduckgo.com", int64(201)) assertExec(t, stmt, 1) } @@ -231,12 +231,12 @@ func TestUpdateWithInvalidModelData(t *testing.T) { MODEL(link). WHERE(Link.ID.EQ(Int(int64(link.Ident)))) - var expectedSql = ` + var expectedSQL = ` UPDATE test_sample.link SET (id, url, name, description, rel) = ('http://www.duckduckgo.com', 'DuckDuckGo', NULL, NULL) WHERE link.id = 201; ` - assertStatementSql(t, stmt, expectedSql, "http://www.duckduckgo.com", "DuckDuckGo", nil, nil, int64(201)) + assertStatementSql(t, stmt, expectedSQL, "http://www.duckduckgo.com", "DuckDuckGo", nil, nil, int64(201)) assertExecErr(t, stmt, "pq: number of columns does not match number of values") } diff --git a/time_expression.go b/time_expression.go index e2c16a8..6d13eba 100644 --- a/time_expression.go +++ b/time_expression.go @@ -1,5 +1,6 @@ package jet +// TimeExpression interface type TimeExpression interface { Expression @@ -68,10 +69,6 @@ func newPrefixTimeExpression(operator string, expression Expression) TimeExpress return &timeExpr } -func INTERVAL(interval string) Expression { - return newPrefixTimeExpression("INTERVAL", literal(interval)) -} - //---------------------------------------------------// type timeExpressionWrapper struct { @@ -85,6 +82,9 @@ func newTimeExpressionWrap(expression Expression) TimeExpression { return &timeExpressionWrap } +// TimeExp is time expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as time expression. +// Does not add sql cast to generated sql builder output. func TimeExp(expression Expression) TimeExpression { return newTimeExpressionWrap(expression) } diff --git a/timestamp_expression.go b/timestamp_expression.go index 9f35ef8..f76c27a 100644 --- a/timestamp_expression.go +++ b/timestamp_expression.go @@ -1,5 +1,6 @@ package jet +// TimestampExpression interface type TimestampExpression interface { Expression @@ -63,6 +64,9 @@ func newTimestampExpressionWrap(expression Expression) TimestampExpression { return ×tampExpressionWrap } +// TimestampExp is timestamp expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as timestamp expression. +// Does not add sql cast to generated sql builder output. func TimestampExp(expression Expression) TimestampExpression { return newTimestampExpressionWrap(expression) } diff --git a/timestampz_expression.go b/timestampz_expression.go index df9457b..1403dfa 100644 --- a/timestampz_expression.go +++ b/timestampz_expression.go @@ -1,5 +1,6 @@ package jet +// TimestampzExpression interface type TimestampzExpression interface { Expression @@ -63,6 +64,9 @@ func newTimestampzExpressionWrap(expression Expression) TimestampzExpression { return ×tampzExpressionWrap } +// TimestampzExp is timestamp with time zone expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as timestamp with time zone expression. +// Does not add sql cast to generated sql builder output. func TimestampzExp(expression Expression) TimestampzExpression { return newTimestampzExpressionWrap(expression) } diff --git a/timez_expression.go b/timez_expression.go index 1ef0eda..cfd1f1d 100644 --- a/timez_expression.go +++ b/timez_expression.go @@ -1,16 +1,25 @@ package jet +// TimezExpression interface 'time with time zone' type TimezExpression interface { Expression + //EQ EQ(rhs TimezExpression) BoolExpression + //NOT_EQ NOT_EQ(rhs TimezExpression) BoolExpression + //IS_DISTINCT_FROM IS_DISTINCT_FROM(rhs TimezExpression) BoolExpression + //IS_NOT_DISTINCT_FROM IS_NOT_DISTINCT_FROM(rhs TimezExpression) BoolExpression + //LT LT(rhs TimezExpression) BoolExpression + //LT_EQ LT_EQ(rhs TimezExpression) BoolExpression + //GT GT(rhs TimezExpression) BoolExpression + //GT_EQ GT_EQ(rhs TimezExpression) BoolExpression } @@ -81,6 +90,9 @@ func newTimezExpressionWrap(expression Expression) TimezExpression { return &timezExpressionWrap } +// TimezExp is time with time zone expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as time with time zone expression. +// Does not add sql cast to generated sql builder output. func TimezExp(expression Expression) TimezExpression { return newTimezExpressionWrap(expression) } diff --git a/update_statement.go b/update_statement.go index 1f342ca..c421ac7 100644 --- a/update_statement.go +++ b/update_statement.go @@ -7,6 +7,7 @@ import ( "github.com/go-jet/jet/execution" ) +// UpdateStatement is interface of SQL UPDATE statement type UpdateStatement interface { Statement @@ -65,7 +66,7 @@ func (u *updateStatementImpl) Sql() (sql string, args []interface{}, err error) return "", nil, errors.New("jet: table to update is nil") } - if err = u.table.serialize(update_statement, out); err != nil { + if err = u.table.serialize(updateStatement, out); err != nil { return } @@ -100,7 +101,7 @@ func (u *updateStatementImpl) Sql() (sql string, args []interface{}, err error) out.writeString("(") } - err = serializeClauseList(update_statement, u.row, out) + err = serializeClauseList(updateStatement, u.row, out) if err != nil { return @@ -114,11 +115,11 @@ func (u *updateStatementImpl) Sql() (sql string, args []interface{}, err error) return "", nil, errors.New("jet: WHERE clause not set") } - if err = out.writeWhere(update_statement, u.where); err != nil { + if err = out.writeWhere(updateStatement, u.where); err != nil { return } - if err = out.writeReturning(update_statement, u.returning); err != nil { + if err = out.writeReturning(updateStatement, u.returning); err != nil { return } @@ -135,7 +136,7 @@ func (u *updateStatementImpl) Query(db execution.DB, destination interface{}) er } func (u *updateStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error { - return queryContext(u, db, context, destination) + return queryContext(context, u, db, destination) } func (u *updateStatementImpl) Exec(db execution.DB) (res sql.Result, err error) { diff --git a/update_statement_test.go b/update_statement_test.go index 9b08675..2647a94 100644 --- a/update_statement_test.go +++ b/update_statement_test.go @@ -5,7 +5,7 @@ import ( ) func TestUpdateWithOneValue(t *testing.T) { - expectedSql := ` + expectedSQL := ` UPDATE db.table1 SET col_int = $1 WHERE table1.col_int >= $2; @@ -14,11 +14,11 @@ WHERE table1.col_int >= $2; SET(1). WHERE(table1ColInt.GT_EQ(Int(33))) - assertStatement(t, stmt, expectedSql, 1, int64(33)) + assertStatement(t, stmt, expectedSQL, 1, int64(33)) } func TestUpdateWithValues(t *testing.T) { - expectedSql := ` + expectedSQL := ` UPDATE db.table1 SET (col_int, col_float) = ($1, $2) WHERE table1.col_int >= $3; @@ -27,11 +27,11 @@ WHERE table1.col_int >= $3; SET(1, 22.2). WHERE(table1ColInt.GT_EQ(Int(33))) - assertStatement(t, stmt, expectedSql, 1, 22.2, int64(33)) + assertStatement(t, stmt, expectedSQL, 1, 22.2, int64(33)) } func TestUpdateOneColumnWithSelect(t *testing.T) { - expectedSql := ` + expectedSQL := ` UPDATE db.table1 SET col_float = ( SELECT table1.col_float AS "table1.col_float" @@ -48,11 +48,11 @@ RETURNING table1.col1 AS "table1.col1"; WHERE(table1Col1.EQ(Int(2))). RETURNING(table1Col1) - assertStatement(t, stmt, expectedSql, int64(2)) + assertStatement(t, stmt, expectedSQL, int64(2)) } func TestUpdateColumnsWithSelect(t *testing.T) { - expectedSql := ` + expectedSQL := ` UPDATE db.table1 SET (col1, col_float) = ( SELECT table1.col_float AS "table1.col_float", @@ -67,7 +67,7 @@ RETURNING table1.col1 AS "table1.col1"; WHERE(table1Col1.EQ(Int(2))). RETURNING(table1Col1) - assertStatement(t, stmt, expectedSql, int64(2)) + assertStatement(t, stmt, expectedSQL, int64(2)) } func TestInvalidInputs(t *testing.T) { diff --git a/utils.go b/utils.go index b7c6299..3dbc467 100644 --- a/utils.go +++ b/utils.go @@ -7,7 +7,7 @@ import ( "strings" ) -func serializeOrderByClauseList(statement statementType, orderByClauses []OrderByClause, out *sqlBuilder) error { +func serializeOrderByClauseList(statement statementType, orderByClauses []orderByClause, out *sqlBuilder) error { for i, value := range orderByClauses { if i > 0 { @@ -32,7 +32,7 @@ func serializeGroupByClauseList(statement statementType, clauses []groupByClause } if c == nil { - return errors.New("jet: nil clause.") + return errors.New("jet: nil clause") } if err = c.serializeForGroupBy(statement, out); err != nil { @@ -51,7 +51,7 @@ func serializeClauseList(statement statementType, clauses []clause, out *sqlBuil } if c == nil { - return errors.New("jet: nil clause.") + return errors.New("jet: nil clause") } if err = c.serialize(statement, out); err != nil { @@ -131,9 +131,9 @@ func isNil(v interface{}) bool { func valueToClause(value interface{}) clause { if clause, ok := value.(clause); ok { return clause - } else { - return literal(value) } + + return literal(value) } func unwindRowFromModel(columns []column, data interface{}) []clause { diff --git a/utils_test.go b/utils_test.go index 22d4f7d..8429349 100644 --- a/utils_test.go +++ b/utils_test.go @@ -53,7 +53,7 @@ var table3 = NewTable( func assertClauseSerialize(t *testing.T, clause clause, query string, args ...interface{}) { out := sqlBuilder{} - err := clause.serialize(select_statement, &out) + err := clause.serialize(selectStatement, &out) assert.NilError(t, err) @@ -63,7 +63,7 @@ func assertClauseSerialize(t *testing.T, clause clause, query string, args ...in func assertClauseSerializeErr(t *testing.T, clause clause, errString string) { out := sqlBuilder{} - err := clause.serialize(select_statement, &out) + err := clause.serialize(selectStatement, &out) //fmt.Println(out.buff.String()) assert.Assert(t, err != nil) @@ -72,7 +72,7 @@ func assertClauseSerializeErr(t *testing.T, clause clause, errString string) { func assertProjectionSerialize(t *testing.T, projection projection, query string, args ...interface{}) { out := sqlBuilder{} - err := projection.serializeForProjection(select_statement, &out) + err := projection.serializeForProjection(selectStatement, &out) assert.NilError(t, err)