Add SQLBuilder support for SQLite databases.
This commit is contained in:
parent
d197956271
commit
e8f4c2b31b
50 changed files with 5851 additions and 75 deletions
55
sqlite/cast.go
Normal file
55
sqlite/cast.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
)
|
||||
|
||||
type cast interface {
|
||||
AS(castType string) Expression
|
||||
AS_TEXT() StringExpression
|
||||
AS_NUMERIC() FloatExpression
|
||||
AS_INTEGER() IntegerExpression
|
||||
AS_REAL() FloatExpression
|
||||
AS_BLOB() StringExpression
|
||||
}
|
||||
|
||||
type castImpl struct {
|
||||
jet.Cast
|
||||
}
|
||||
|
||||
// CAST function converts a expr (of any type) into latter specified datatype.
|
||||
func CAST(expr Expression) cast {
|
||||
castImpl := &castImpl{}
|
||||
castImpl.Cast = jet.NewCastImpl(expr)
|
||||
return castImpl
|
||||
}
|
||||
|
||||
// AS casts expressions to castType
|
||||
func (c *castImpl) AS(castType string) Expression {
|
||||
return c.Cast.AS(castType)
|
||||
}
|
||||
|
||||
// AS_TEXT cast expression to TEXT type
|
||||
func (c *castImpl) AS_TEXT() StringExpression {
|
||||
return StringExp(c.AS("TEXT"))
|
||||
}
|
||||
|
||||
// AS_NUMERIC cast expression to NUMERIC type
|
||||
func (c *castImpl) AS_NUMERIC() FloatExpression {
|
||||
return FloatExp(c.AS("NUMERIC"))
|
||||
}
|
||||
|
||||
// AS_INTEGER cast expression to INTEGER type
|
||||
func (c *castImpl) AS_INTEGER() IntegerExpression {
|
||||
return IntExp(c.AS("INTEGER"))
|
||||
}
|
||||
|
||||
// AS_REAL cast expression to REAL type
|
||||
func (c *castImpl) AS_REAL() FloatExpression {
|
||||
return FloatExp(c.AS("REAL"))
|
||||
}
|
||||
|
||||
// AS_BLOB cast expression to BLOB type
|
||||
func (c *castImpl) AS_BLOB() StringExpression {
|
||||
return StringExp(c.AS("BLOB"))
|
||||
}
|
||||
14
sqlite/cast_test.go
Normal file
14
sqlite/cast_test.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCAST(t *testing.T) {
|
||||
assertSerialize(t, CAST(Float(11.22)).AS("bigint"), `CAST(? AS bigint)`)
|
||||
assertSerialize(t, CAST(Int(22)).AS_TEXT(), `CAST(? AS TEXT)`)
|
||||
assertSerialize(t, CAST(Int(22)).AS_NUMERIC(), `CAST(? AS NUMERIC)`)
|
||||
assertSerialize(t, CAST(String("22")).AS_INTEGER(), `CAST(? AS INTEGER)`)
|
||||
assertSerialize(t, CAST(String("22.2")).AS_REAL(), `CAST(? AS REAL)`)
|
||||
assertSerialize(t, CAST(String("blob")).AS_BLOB(), `CAST(? AS BLOB)`)
|
||||
}
|
||||
58
sqlite/columns.go
Normal file
58
sqlite/columns.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// Column is common column interface for all types of columns.
|
||||
type Column = jet.ColumnExpression
|
||||
|
||||
// ColumnList function returns list of columns that be used as projection or column list for UPDATE and INSERT statement.
|
||||
type ColumnList = jet.ColumnList
|
||||
|
||||
// ColumnBool is interface for SQL boolean columns.
|
||||
type ColumnBool = jet.ColumnBool
|
||||
|
||||
// BoolColumn creates named bool column.
|
||||
var BoolColumn = jet.BoolColumn
|
||||
|
||||
// ColumnString is interface for SQL text, character, character varying
|
||||
// bytea, uuid columns and enums types.
|
||||
type ColumnString = jet.ColumnString
|
||||
|
||||
// StringColumn creates named string column.
|
||||
var StringColumn = jet.StringColumn
|
||||
|
||||
// ColumnInteger is interface for SQL smallint, integer, bigint columns.
|
||||
type ColumnInteger = jet.ColumnInteger
|
||||
|
||||
// IntegerColumn creates named integer column.
|
||||
var IntegerColumn = jet.IntegerColumn
|
||||
|
||||
// ColumnFloat is interface for SQL real, numeric, decimal or double precision column.
|
||||
type ColumnFloat = jet.ColumnFloat
|
||||
|
||||
// FloatColumn creates named float column.
|
||||
var FloatColumn = jet.FloatColumn
|
||||
|
||||
// ColumnTime is interface for SQL time column.
|
||||
type ColumnTime = jet.ColumnTime
|
||||
|
||||
// TimeColumn creates named time column
|
||||
var TimeColumn = jet.TimeColumn
|
||||
|
||||
// ColumnDate is interface of SQL date columns.
|
||||
type ColumnDate = jet.ColumnDate
|
||||
|
||||
// DateColumn creates named date column.
|
||||
var DateColumn = jet.DateColumn
|
||||
|
||||
// ColumnDateTime is interface of SQL timestamp columns.
|
||||
type ColumnDateTime = jet.ColumnTimestamp
|
||||
|
||||
// DateTimeColumn creates named timestamp column
|
||||
var DateTimeColumn = jet.TimestampColumn
|
||||
|
||||
//ColumnTimestamp is interface of SQL timestamp columns.
|
||||
type ColumnTimestamp = jet.ColumnTimestamp
|
||||
|
||||
// TimestampColumn creates named timestamp column
|
||||
var TimestampColumn = jet.TimestampColumn
|
||||
61
sqlite/delete_statement.go
Normal file
61
sqlite/delete_statement.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// DeleteStatement is interface for MySQL DELETE statement
|
||||
type DeleteStatement interface {
|
||||
Statement
|
||||
|
||||
WHERE(expression BoolExpression) DeleteStatement
|
||||
ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement
|
||||
LIMIT(limit int64) DeleteStatement
|
||||
RETURNING(projections ...jet.Projection) DeleteStatement
|
||||
}
|
||||
|
||||
type deleteStatementImpl struct {
|
||||
jet.SerializerStatement
|
||||
|
||||
Delete jet.ClauseStatementBegin
|
||||
Where jet.ClauseWhere
|
||||
OrderBy jet.ClauseOrderBy
|
||||
Limit jet.ClauseLimit
|
||||
Returning jet.ClauseReturning
|
||||
}
|
||||
|
||||
func newDeleteStatement(table Table) DeleteStatement {
|
||||
newDelete := &deleteStatementImpl{}
|
||||
newDelete.SerializerStatement = jet.NewStatementImpl(Dialect, jet.DeleteStatementType, newDelete,
|
||||
&newDelete.Delete,
|
||||
&newDelete.Where,
|
||||
&newDelete.OrderBy,
|
||||
&newDelete.Limit,
|
||||
&newDelete.Returning,
|
||||
)
|
||||
|
||||
newDelete.Delete.Name = "DELETE FROM"
|
||||
newDelete.Delete.Tables = append(newDelete.Delete.Tables, table)
|
||||
newDelete.Where.Mandatory = true
|
||||
newDelete.Limit.Count = -1
|
||||
|
||||
return newDelete
|
||||
}
|
||||
|
||||
func (d *deleteStatementImpl) WHERE(expression BoolExpression) DeleteStatement {
|
||||
d.Where.Condition = expression
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *deleteStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement {
|
||||
d.OrderBy.List = orderByClauses
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *deleteStatementImpl) LIMIT(limit int64) DeleteStatement {
|
||||
d.Limit.Count = limit
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *deleteStatementImpl) RETURNING(projections ...jet.Projection) DeleteStatement {
|
||||
d.Returning.ProjectionList = projections
|
||||
return d
|
||||
}
|
||||
26
sqlite/delete_statement_test.go
Normal file
26
sqlite/delete_statement_test.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeleteUnconditionally(t *testing.T) {
|
||||
assertStatementSqlErr(t, table1.DELETE(), `jet: WHERE clause not set`)
|
||||
assertStatementSqlErr(t, table1.DELETE().WHERE(nil), `jet: WHERE clause not set`)
|
||||
}
|
||||
|
||||
func TestDeleteWithWhere(t *testing.T) {
|
||||
assertStatementSql(t, table1.DELETE().WHERE(table1Col1.EQ(Int(1))), `
|
||||
DELETE FROM db.table1
|
||||
WHERE table1.col1 = ?;
|
||||
`, int64(1))
|
||||
}
|
||||
|
||||
func TestDeleteWithWhereOrderByLimit(t *testing.T) {
|
||||
assertStatementSql(t, table1.DELETE().WHERE(table1Col1.EQ(Int(1))).ORDER_BY(table1Col1).LIMIT(1), `
|
||||
DELETE FROM db.table1
|
||||
WHERE table1.col1 = ?
|
||||
ORDER BY table1.col1
|
||||
LIMIT ?;
|
||||
`, int64(1), int64(1))
|
||||
}
|
||||
225
sqlite/dialect.go
Normal file
225
sqlite/dialect.go
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
)
|
||||
|
||||
// Dialect is implementation of SQL Builder for SQLite databases.
|
||||
var Dialect = newDialect()
|
||||
|
||||
func newDialect() jet.Dialect {
|
||||
operatorSerializeOverrides := map[string]jet.SerializeOverride{}
|
||||
operatorSerializeOverrides["IS DISTINCT FROM"] = sqlite_IS_DISTINCT_FROM
|
||||
operatorSerializeOverrides["IS NOT DISTINCT FROM"] = sqlite_IS_NOT_DISTINCT_FROM
|
||||
operatorSerializeOverrides["#"] = sqliteBitXOR
|
||||
|
||||
mySQLDialectParams := jet.DialectParams{
|
||||
Name: "SQLite",
|
||||
PackageName: "sqlite",
|
||||
OperatorSerializeOverrides: operatorSerializeOverrides,
|
||||
AliasQuoteChar: '"',
|
||||
IdentifierQuoteChar: '`',
|
||||
ArgumentPlaceholder: func(int) string {
|
||||
return "?"
|
||||
},
|
||||
ReservedWords: reservedWords2,
|
||||
}
|
||||
|
||||
return jet.NewDialect(mySQLDialectParams)
|
||||
}
|
||||
|
||||
func sqliteBitXOR(expressions ...jet.Serializer) jet.SerializerFunc {
|
||||
return func(statement jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) {
|
||||
if len(expressions) < 2 {
|
||||
panic("jet: invalid number of expressions for operator XOR")
|
||||
}
|
||||
|
||||
// (~(a&b))&(a|b)
|
||||
a := expressions[0]
|
||||
b := expressions[1]
|
||||
|
||||
out.WriteString("(~(")
|
||||
jet.Serialize(a, statement, out, options...)
|
||||
out.WriteByte('&')
|
||||
jet.Serialize(b, statement, out, options...)
|
||||
out.WriteString("))&(")
|
||||
jet.Serialize(a, statement, out, options...)
|
||||
out.WriteByte('|')
|
||||
jet.Serialize(b, statement, out, options...)
|
||||
out.WriteByte(')')
|
||||
}
|
||||
}
|
||||
|
||||
func sqlite_IS_NOT_DISTINCT_FROM(expressions ...jet.Serializer) jet.SerializerFunc {
|
||||
return func(statement jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) {
|
||||
if len(expressions) < 2 {
|
||||
panic("jet: invalid number of expressions for operator")
|
||||
}
|
||||
|
||||
jet.Serialize(expressions[0], statement, out)
|
||||
out.WriteString("IS")
|
||||
jet.Serialize(expressions[1], statement, out)
|
||||
}
|
||||
}
|
||||
|
||||
func sqlite_IS_DISTINCT_FROM(expressions ...jet.Serializer) jet.SerializerFunc {
|
||||
return func(statement jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) {
|
||||
if len(expressions) < 2 {
|
||||
panic("jet: invalid number of expressions for operator")
|
||||
}
|
||||
|
||||
jet.Serialize(expressions[0], statement, out)
|
||||
out.WriteString("IS NOT")
|
||||
jet.Serialize(expressions[1], statement, out)
|
||||
}
|
||||
}
|
||||
|
||||
var reservedWords2 = []string{
|
||||
"ABORT",
|
||||
"ACTION",
|
||||
"ADD",
|
||||
"AFTER",
|
||||
"ALL",
|
||||
"ALTER",
|
||||
"ALWAYS",
|
||||
"ANALYZE",
|
||||
"AND",
|
||||
"AS",
|
||||
"ASC",
|
||||
"ATTACH",
|
||||
"AUTOINCREMENT",
|
||||
"BEFORE",
|
||||
"BEGIN",
|
||||
"BETWEEN",
|
||||
"BY",
|
||||
"CASCADE",
|
||||
"CASE",
|
||||
"CAST",
|
||||
"CHECK",
|
||||
"COLLATE",
|
||||
"COLUMN",
|
||||
"COMMIT",
|
||||
"CONFLICT",
|
||||
"CONSTRAINT",
|
||||
"CREATE",
|
||||
"CROSS",
|
||||
"CURRENT",
|
||||
"CURRENT_DATE",
|
||||
"CURRENT_TIME",
|
||||
"CURRENT_TIMESTAMP",
|
||||
"DATABASE",
|
||||
"DEFAULT",
|
||||
"DEFERRABLE",
|
||||
"DEFERRED",
|
||||
"DELETE",
|
||||
"DESC",
|
||||
"DETACH",
|
||||
"DISTINCT",
|
||||
"DO",
|
||||
"DROP",
|
||||
"EACH",
|
||||
"ELSE",
|
||||
"END",
|
||||
"ESCAPE",
|
||||
"EXCEPT",
|
||||
"EXCLUDE",
|
||||
"EXCLUSIVE",
|
||||
"EXISTS",
|
||||
"EXPLAIN",
|
||||
"FAIL",
|
||||
"FILTER",
|
||||
"FIRST",
|
||||
"FOLLOWING",
|
||||
"FOR",
|
||||
"FOREIGN",
|
||||
"FROM",
|
||||
"FULL",
|
||||
"GENERATED",
|
||||
"GLOB",
|
||||
"GROUP",
|
||||
"GROUPS",
|
||||
"HAVING",
|
||||
"IF",
|
||||
"IGNORE",
|
||||
"IMMEDIATE",
|
||||
"IN",
|
||||
"INDEX",
|
||||
"INDEXED",
|
||||
"INITIALLY",
|
||||
"INNER",
|
||||
"INSERT",
|
||||
"INSTEAD",
|
||||
"INTERSECT",
|
||||
"INTO",
|
||||
"IS",
|
||||
"ISNULL",
|
||||
"JOIN",
|
||||
"KEY",
|
||||
"LAST",
|
||||
"LEFT",
|
||||
"LIKE",
|
||||
"LIMIT",
|
||||
"MATCH",
|
||||
"MATERIALIZED",
|
||||
"NATURAL",
|
||||
"NO",
|
||||
"NOT",
|
||||
"NOTHING",
|
||||
"NOTNULL",
|
||||
"NULL",
|
||||
"NULLS",
|
||||
"OF",
|
||||
"OFFSET",
|
||||
"ON",
|
||||
"OR",
|
||||
"ORDER",
|
||||
"OTHERS",
|
||||
"OUTER",
|
||||
"OVER",
|
||||
"PARTITION",
|
||||
"PLAN",
|
||||
"PRAGMA",
|
||||
"PRECEDING",
|
||||
"PRIMARY",
|
||||
"QUERY",
|
||||
"RAISE",
|
||||
"RANGE",
|
||||
"RECURSIVE",
|
||||
"REFERENCES",
|
||||
"REGEXP",
|
||||
"REINDEX",
|
||||
"RELEASE",
|
||||
"RENAME",
|
||||
"REPLACE",
|
||||
"RESTRICT",
|
||||
"RETURNING",
|
||||
"RIGHT",
|
||||
"ROLLBACK",
|
||||
"ROW",
|
||||
"ROWS",
|
||||
"SAVEPOINT",
|
||||
"SELECT",
|
||||
"SET",
|
||||
"TABLE",
|
||||
"TEMP",
|
||||
"TEMPORARY",
|
||||
"THEN",
|
||||
"TIES",
|
||||
"TO",
|
||||
"TRANSACTION",
|
||||
"TRIGGER",
|
||||
"UNBOUNDED",
|
||||
"UNION",
|
||||
"UNIQUE",
|
||||
"UPDATE",
|
||||
"USING",
|
||||
"VACUUM",
|
||||
"VALUES",
|
||||
"VIEW",
|
||||
"VIRTUAL",
|
||||
"WHEN",
|
||||
"WHERE",
|
||||
"WINDOW",
|
||||
"WITH",
|
||||
"WITHOUT",
|
||||
}
|
||||
59
sqlite/dialect_test.go
Normal file
59
sqlite/dialect_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBoolExpressionIS_DISTINCT_FROM(t *testing.T) {
|
||||
assertSerialize(t, table1ColBool.IS_DISTINCT_FROM(table2ColBool), "(table1.col_bool IS NOT table2.col_bool)")
|
||||
assertSerialize(t, table1ColBool.IS_DISTINCT_FROM(Bool(false)), "(table1.col_bool IS NOT ?)", false)
|
||||
}
|
||||
|
||||
func TestBoolExpressionIS_NOT_DISTINCT_FROM(t *testing.T) {
|
||||
assertSerialize(t, table1ColBool.IS_NOT_DISTINCT_FROM(table2ColBool), "(table1.col_bool IS table2.col_bool)")
|
||||
assertSerialize(t, table1ColBool.IS_NOT_DISTINCT_FROM(Bool(false)), "(table1.col_bool IS ?)", false)
|
||||
}
|
||||
|
||||
func TestBoolLiteral(t *testing.T) {
|
||||
assertSerialize(t, Bool(true), "?", true)
|
||||
assertSerialize(t, Bool(false), "?", false)
|
||||
}
|
||||
|
||||
func TestIntegerExpressionDIV(t *testing.T) {
|
||||
assertSerialize(t, table1ColInt.DIV(table2ColInt), "(table1.col_int / table2.col_int)")
|
||||
assertSerialize(t, table1ColInt.DIV(Int(11)), "(table1.col_int / ?)", int64(11))
|
||||
}
|
||||
|
||||
func TestIntExpressionPOW(t *testing.T) {
|
||||
assertSerialize(t, table1ColInt.POW(table2ColInt), "POW(table1.col_int, table2.col_int)")
|
||||
assertSerialize(t, table1ColInt.POW(Int(11)), "POW(table1.col_int, ?)", int64(11))
|
||||
}
|
||||
|
||||
func TestIntExpressionBIT_XOR(t *testing.T) {
|
||||
assertSerialize(t, table1ColInt.BIT_XOR(table2ColInt), "((~(table1.col_int & table2.col_int))&(table1.col_int | table2.col_int))")
|
||||
assertSerialize(t, table1ColInt.BIT_XOR(Int(11)), "((~(table1.col_int & ?))&(table1.col_int | ?))", int64(11), int64(11))
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
assertSerialize(t, EXISTS(
|
||||
table2.
|
||||
SELECT(Int(1)).
|
||||
WHERE(table1Col1.EQ(table2Col3)),
|
||||
),
|
||||
`(EXISTS (
|
||||
SELECT ?
|
||||
FROM db.table2
|
||||
WHERE table1.col1 = table2.col3
|
||||
))`, int64(1))
|
||||
}
|
||||
|
||||
func TestString_REGEXP_LIKE_operator(t *testing.T) {
|
||||
assertSerialize(t, table3StrCol.REGEXP_LIKE(table2ColStr), "(table3.col2 REGEXP table2.col_str)")
|
||||
assertSerialize(t, table3StrCol.REGEXP_LIKE(String("JOHN")), "(table3.col2 REGEXP ?)", "JOHN")
|
||||
|
||||
}
|
||||
|
||||
func TestString_NOT_REGEXP_LIKE_operator(t *testing.T) {
|
||||
assertSerialize(t, table3StrCol.NOT_REGEXP_LIKE(table2ColStr), "(table3.col2 NOT REGEXP table2.col_str)")
|
||||
assertSerialize(t, table3StrCol.NOT_REGEXP_LIKE(String("JOHN")), "(table3.col2 NOT REGEXP ?)", "JOHN")
|
||||
}
|
||||
97
sqlite/expressions.go
Normal file
97
sqlite/expressions.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// Expression is common interface for all expressions.
|
||||
// Can be Bool, Int, Float, String, Date, Time or Timestamp expressions.
|
||||
type Expression = jet.Expression
|
||||
|
||||
// BoolExpression interface
|
||||
type BoolExpression = jet.BoolExpression
|
||||
|
||||
// StringExpression interface
|
||||
type StringExpression = jet.StringExpression
|
||||
|
||||
// NumericExpression is shared interface for integer or real expression
|
||||
type NumericExpression = jet.NumericExpression
|
||||
|
||||
// IntegerExpression interface
|
||||
type IntegerExpression = jet.IntegerExpression
|
||||
|
||||
// FloatExpression interface
|
||||
type FloatExpression = jet.FloatExpression
|
||||
|
||||
// TimeExpression interface
|
||||
type TimeExpression = jet.TimeExpression
|
||||
|
||||
// DateExpression interface
|
||||
type DateExpression = jet.DateExpression
|
||||
|
||||
// DateTimeExpression interface
|
||||
type DateTimeExpression = jet.TimestampExpression
|
||||
|
||||
// TimestampExpression interface
|
||||
type TimestampExpression = jet.TimestampExpression
|
||||
|
||||
// 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.
|
||||
var BoolExp = jet.BoolExp
|
||||
|
||||
// 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.
|
||||
var StringExp = jet.StringExp
|
||||
|
||||
// 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.
|
||||
var IntExp = jet.IntExp
|
||||
|
||||
// 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.
|
||||
var FloatExp = jet.FloatExp
|
||||
|
||||
// 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.
|
||||
var TimeExp = jet.TimeExp
|
||||
|
||||
// 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.
|
||||
var DateExp = jet.DateExp
|
||||
|
||||
// DateTimeExp 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.
|
||||
var DateTimeExp = jet.TimestampExp
|
||||
|
||||
// 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.
|
||||
var TimestampExp = jet.TimestampExp
|
||||
|
||||
// RawArgs is type used to pass optional arguments to Raw method
|
||||
type RawArgs = map[string]interface{}
|
||||
|
||||
// Raw can be used for any unsupported functions, operators or expressions.
|
||||
// For example: Raw("current_database()")
|
||||
// Raw helper methods for each of the sqlite types
|
||||
var (
|
||||
Raw = jet.Raw
|
||||
|
||||
RawInt = jet.RawInt
|
||||
RawFloat = jet.RawFloat
|
||||
RawString = jet.RawString
|
||||
RawTime = jet.RawTime
|
||||
RawTimestamp = jet.RawTimestamp
|
||||
RawDate = jet.RawDate
|
||||
)
|
||||
|
||||
// Func can be used to call an custom or as of yet unsupported function in the database.
|
||||
var Func = jet.Func
|
||||
|
||||
// NewEnumValue creates new named enum value
|
||||
var NewEnumValue = jet.NewEnumValue
|
||||
52
sqlite/expressions_test.go
Normal file
52
sqlite/expressions_test.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRaw(t *testing.T) {
|
||||
assertSerialize(t, Raw("current_database()"), "(current_database())")
|
||||
assertDebugSerialize(t, Raw("current_database()"), "(current_database())")
|
||||
|
||||
assertSerialize(t, Raw(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22}),
|
||||
"(? + table.colInt + ?)", 11, 22)
|
||||
assertDebugSerialize(t, Raw(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22}),
|
||||
"(11 + table.colInt + 22)")
|
||||
|
||||
assertSerialize(t,
|
||||
Int(700).ADD(RawInt("#1 + table.colInt + #2", RawArgs{"#1": 11, "#2": 22})),
|
||||
"(? + (? + table.colInt + ?))",
|
||||
int64(700), 11, 22)
|
||||
assertDebugSerialize(t,
|
||||
Int(700).ADD(RawInt("#1 + table.colInt + #2", RawArgs{"#1": 11, "#2": 22})),
|
||||
"(700 + (11 + table.colInt + 22))")
|
||||
}
|
||||
|
||||
func TestRawDuplicateArguments(t *testing.T) {
|
||||
assertSerialize(t, Raw(":arg + table.colInt + :arg", RawArgs{":arg": 11}),
|
||||
"(? + table.colInt + ?)", 11, 11)
|
||||
|
||||
assertSerialize(t, Raw("#age + table.colInt + #year + #age + #year + 11", RawArgs{"#age": 11, "#year": 2000}),
|
||||
"(? + table.colInt + ? + ? + ? + 11)", 11, 2000, 11, 2000)
|
||||
|
||||
assertSerialize(t, Raw("#1 + all_types.integer + #2 + #1 + #2 + #3 + #4",
|
||||
RawArgs{"#1": 11, "#2": 22, "#3": 33, "#4": 44}),
|
||||
`(? + all_types.integer + ? + ? + ? + ? + ?)`, 11, 22, 11, 22, 33, 44)
|
||||
}
|
||||
|
||||
func TestRawInvalidArguments(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
require.Equal(t, "jet: named argument 'first_arg' does not appear in raw query", r)
|
||||
}()
|
||||
|
||||
assertSerialize(t, Raw("table.colInt + :second_arg", RawArgs{"first_arg": 11}), "(table.colInt + ?)", 22)
|
||||
}
|
||||
|
||||
func TestRawType(t *testing.T) {
|
||||
assertSerialize(t, RawFloat("table.colInt + &float", RawArgs{"&float": 11.22}).EQ(Float(3.14)),
|
||||
"((table.colInt + ?) = ?)", 11.22, 3.14)
|
||||
assertSerialize(t, RawString("table.colStr || str", RawArgs{"str": "doe"}).EQ(String("john doe")),
|
||||
"((table.colStr || ?) = ?)", "doe", "john doe")
|
||||
}
|
||||
342
sqlite/functions.go
Normal file
342
sqlite/functions.go
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ROW is construct one table row from list of expressions.
|
||||
func ROW(expressions ...Expression) Expression {
|
||||
return jet.NewFunc("", expressions, nil)
|
||||
}
|
||||
|
||||
// ------------------ Mathematical functions ---------------//
|
||||
|
||||
// ABSf calculates absolute value from float expression
|
||||
var ABSf = jet.ABSf
|
||||
|
||||
// ABSi calculates absolute value from int expression
|
||||
var ABSi = jet.ABSi
|
||||
|
||||
// POW calculates power of base with exponent
|
||||
var POW = jet.POW
|
||||
|
||||
// POWER calculates power of base with exponent
|
||||
var POWER = jet.POWER
|
||||
|
||||
// SQRT calculates square root of numeric expression
|
||||
var SQRT = jet.SQRT
|
||||
|
||||
// CBRT calculates cube root of numeric expression
|
||||
func CBRT(number jet.NumericExpression) jet.FloatExpression {
|
||||
return POWER(number, Float(1.0).DIV(Float(3.0)))
|
||||
}
|
||||
|
||||
// CEIL calculates ceil of float expression
|
||||
var CEIL = jet.CEIL
|
||||
|
||||
// FLOOR calculates floor of float expression
|
||||
var FLOOR = jet.FLOOR
|
||||
|
||||
// ROUND calculates round of a float expressions with optional precision
|
||||
var ROUND = jet.ROUND
|
||||
|
||||
// SIGN returns sign of float expression
|
||||
var SIGN = jet.SIGN
|
||||
|
||||
// TRUNC calculates trunc of float expression with precision
|
||||
var TRUNC = TRUNCATE
|
||||
|
||||
// TRUNCATE calculates trunc of float expression with precision
|
||||
var TRUNCATE = func(floatExpression jet.FloatExpression, precision jet.IntegerExpression) jet.FloatExpression {
|
||||
return jet.NewFloatFunc("TRUNCATE", floatExpression, precision)
|
||||
}
|
||||
|
||||
// LN calculates natural algorithm of float expression
|
||||
var LN = jet.LN
|
||||
|
||||
// LOG calculates logarithm of float expression
|
||||
var LOG = jet.LOG
|
||||
|
||||
// ----------------- Aggregate functions -------------------//
|
||||
|
||||
// AVG is aggregate function used to calculate avg value from numeric expression
|
||||
var AVG = jet.AVG
|
||||
|
||||
// BIT_AND is aggregate function used to calculates the bitwise AND of all non-null input values, or null if none.
|
||||
//var BIT_AND = jet.BIT_AND
|
||||
|
||||
// BIT_OR is aggregate function used to calculates the bitwise OR of all non-null input values, or null if none.
|
||||
//var BIT_OR = jet.BIT_OR
|
||||
|
||||
// COUNT is aggregate function. Returns number of input rows for which the value of expression is not null.
|
||||
var COUNT = jet.COUNT
|
||||
|
||||
// MAX is aggregate function. Returns maximum value of expression across all input values
|
||||
var MAX = jet.MAX
|
||||
|
||||
// MAXi is aggregate function. Returns maximum value of int expression across all input values
|
||||
var MAXi = jet.MAXi
|
||||
|
||||
// MAXf is aggregate function. Returns maximum value of float expression across all input values
|
||||
var MAXf = jet.MAXf
|
||||
|
||||
// MIN is aggregate function. Returns minimum value of int expression across all input values
|
||||
var MIN = jet.MIN
|
||||
|
||||
// MINi is aggregate function. Returns minimum value of int expression across all input values
|
||||
var MINi = jet.MINi
|
||||
|
||||
// MINf is aggregate function. Returns minimum value of float expression across all input values
|
||||
var MINf = jet.MINf
|
||||
|
||||
// SUM is aggregate function. Returns sum of all expressions
|
||||
var SUM = jet.SUM
|
||||
|
||||
// SUMi is aggregate function. Returns sum of integer expression.
|
||||
var SUMi = jet.SUMi
|
||||
|
||||
// SUMf is aggregate function. Returns sum of float expression.
|
||||
var SUMf = jet.SUMf
|
||||
|
||||
// -------------------- Window functions -----------------------//
|
||||
|
||||
// ROW_NUMBER returns number of the current row within its partition, counting from 1
|
||||
var ROW_NUMBER = jet.ROW_NUMBER
|
||||
|
||||
// RANK of the current row with gaps; same as row_number of its first peer
|
||||
var RANK = jet.RANK
|
||||
|
||||
// DENSE_RANK returns rank of the current row without gaps; this function counts peer groups
|
||||
var DENSE_RANK = jet.DENSE_RANK
|
||||
|
||||
// PERCENT_RANK calculates relative rank of the current row: (rank - 1) / (total partition rows - 1)
|
||||
var PERCENT_RANK = jet.PERCENT_RANK
|
||||
|
||||
// CUME_DIST calculates cumulative distribution: (number of partition rows preceding or peer with current row) / total partition rows
|
||||
var CUME_DIST = jet.CUME_DIST
|
||||
|
||||
// NTILE returns integer ranging from 1 to the argument value, dividing the partition as equally as possible
|
||||
var NTILE = jet.NTILE
|
||||
|
||||
// LAG returns value evaluated at the row that is offset rows before the current row within the partition;
|
||||
// if there is no such row, instead return default (which must be of the same type as value).
|
||||
// Both offset and default are evaluated with respect to the current row.
|
||||
// If omitted, offset defaults to 1 and default to null
|
||||
var LAG = jet.LAG
|
||||
|
||||
// LEAD returns value evaluated at the row that is offset rows after the current row within the partition;
|
||||
// if there is no such row, instead return default (which must be of the same type as value).
|
||||
// Both offset and default are evaluated with respect to the current row.
|
||||
// If omitted, offset defaults to 1 and default to null
|
||||
var LEAD = jet.LEAD
|
||||
|
||||
// FIRST_VALUE returns value evaluated at the row that is the first row of the window frame
|
||||
var FIRST_VALUE = jet.FIRST_VALUE
|
||||
|
||||
// LAST_VALUE returns value evaluated at the row that is the last row of the window frame
|
||||
var LAST_VALUE = jet.LAST_VALUE
|
||||
|
||||
// NTH_VALUE returns value evaluated at the row that is the nth row of the window frame (counting from 1); null if no such row
|
||||
var NTH_VALUE = jet.NTH_VALUE
|
||||
|
||||
//--------------------- String functions ------------------//
|
||||
|
||||
// BIT_LENGTH returns number of bits in string expression
|
||||
//var BIT_LENGTH = jet.BIT_LENGTH
|
||||
//
|
||||
//// CHAR_LENGTH returns number of characters in string expression
|
||||
//var CHAR_LENGTH = jet.CHAR_LENGTH
|
||||
//
|
||||
//// OCTET_LENGTH returns number of bytes in string expression
|
||||
//var OCTET_LENGTH = jet.OCTET_LENGTH
|
||||
|
||||
// LOWER returns string expression in lower case
|
||||
var LOWER = jet.LOWER
|
||||
|
||||
// UPPER returns string expression in upper case
|
||||
var UPPER = jet.UPPER
|
||||
|
||||
// LTRIM removes the longest string containing only characters
|
||||
// from characters (a space by default) from the start of string
|
||||
var LTRIM = jet.LTRIM
|
||||
|
||||
// RTRIM removes the longest string containing only characters
|
||||
// from characters (a space by default) from the end of string
|
||||
var RTRIM = jet.RTRIM
|
||||
|
||||
// CONCAT adds two or more expressions together
|
||||
//var CONCAT = jet.CONCAT
|
||||
|
||||
// CONCAT_WS adds two or more expressions together with a separator.
|
||||
//var CONCAT_WS = jet.CONCAT_WS
|
||||
|
||||
// FORMAT formats a number to a format like "#,###,###.##", rounded to a specified number of decimal places, then it returns the result as a string.
|
||||
//var FORMAT = jet.FORMAT
|
||||
|
||||
// LEFTSTR returns first n characters in the string.
|
||||
// When n is negative, return all but last |n| characters.
|
||||
//func LEFTSTR(str StringExpression, n IntegerExpression) StringExpression {
|
||||
// return jet.NewStringFunc("LEFTSTR", str, n)
|
||||
//}
|
||||
//
|
||||
//// RIGHT returns last n characters in the string.
|
||||
//// When n is negative, return all but first |n| characters.
|
||||
//func RIGHTSTR(str StringExpression, n IntegerExpression) StringExpression {
|
||||
// return jet.NewStringFunc("RIGHTSTR", str, n)
|
||||
//}
|
||||
|
||||
// LENGTH returns number of characters in string with a given encoding
|
||||
func LENGTH(str jet.StringExpression) jet.StringExpression {
|
||||
return jet.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 jet.StringExpression, length jet.IntegerExpression, text jet.StringExpression) jet.StringExpression {
|
||||
// return jet.LPAD(str, length, text)
|
||||
//}
|
||||
|
||||
// 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 jet.StringExpression, length jet.IntegerExpression, text jet.StringExpression) jet.StringExpression {
|
||||
// return jet.RPAD(str, length, text)
|
||||
//}
|
||||
|
||||
// MD5 calculates the MD5 hash of string, returning the result in hexadecimal
|
||||
//var MD5 = jet.MD5
|
||||
|
||||
// REPEAT repeats string the specified number of times
|
||||
//var REPEAT = jet.REPEAT
|
||||
|
||||
// REPLACE replaces all occurrences in string of substring from with substring to
|
||||
var REPLACE = jet.REPLACE
|
||||
|
||||
// REVERSE returns reversed string.
|
||||
var REVERSE = jet.REVERSE
|
||||
|
||||
// SUBSTR extracts substring
|
||||
var SUBSTR = jet.SUBSTR
|
||||
|
||||
// REGEXP_LIKE Returns 1 if the string expr matches the regular expression specified by the pattern pat, 0 otherwise.
|
||||
var REGEXP_LIKE = jet.REGEXP_LIKE
|
||||
|
||||
//----------------- Date/Time Functions and Operators ------------//
|
||||
|
||||
// CURRENT_DATE returns current date
|
||||
var CURRENT_DATE = jet.CURRENT_DATE
|
||||
|
||||
// CURRENT_TIME returns current time with time zone
|
||||
func CURRENT_TIME() TimeExpression {
|
||||
return TimeExp(jet.CURRENT_TIME())
|
||||
}
|
||||
|
||||
// CURRENT_TIMESTAMP returns current timestamp with time zone
|
||||
func CURRENT_TIMESTAMP() TimestampExpression {
|
||||
return TimestampExp(jet.CURRENT_TIMESTAMP())
|
||||
}
|
||||
|
||||
//// NOW returns current datetime
|
||||
//func NOW() DateTimeExpression {
|
||||
// //if len(fsp) > 0 {
|
||||
// // return jet.NewTimestampFunc("NOW", jet.FixedLiteral(int64(fsp[0])))
|
||||
// //}
|
||||
// //return jet.NewTimestampFunc("NOW")
|
||||
// return DATETIME(jet.FixedLiteral("now"))
|
||||
//}
|
||||
|
||||
// time-value modifiers
|
||||
var (
|
||||
YEARS = modifier("YEARS")
|
||||
MONTHS = modifier("MONTHS")
|
||||
DAYS = modifier("DAYS")
|
||||
HOURS = modifier("HOURS")
|
||||
MINUTES = modifier("MINUTES")
|
||||
SECONDS = modifier("SECONDS")
|
||||
|
||||
START_OF_YEAR = String("start of year")
|
||||
START_OF_MONTH = String("start of month")
|
||||
UNIXEPOCH = String("unixepoch")
|
||||
LOCALTIME = String("localtime")
|
||||
UTC = String("UTC")
|
||||
|
||||
WEEKDAY = func(value int) Expression {
|
||||
return String(fmt.Sprintf("WEEKDAY %d", value))
|
||||
}
|
||||
)
|
||||
|
||||
func modifier(modifierName string) func(value float64) Expression {
|
||||
return func(value float64) Expression {
|
||||
return String(fmt.Sprintf("%g %s", value, modifierName))
|
||||
}
|
||||
}
|
||||
|
||||
// DATE function creates new date from time-value and zero or more time modifiers
|
||||
func DATE(timeValue interface{}, modifiers ...Expression) DateExpression {
|
||||
exprList := getFuncExprList(timeValue, modifiers...)
|
||||
|
||||
return jet.NewDateFunc("DATE", exprList...)
|
||||
}
|
||||
|
||||
// TIME function creates new time from time-value and zero or more time modifiers
|
||||
func TIME(timeValue interface{}, modifiers ...Expression) TimeExpression {
|
||||
exprList := getFuncExprList(timeValue, modifiers...)
|
||||
|
||||
return jet.NewTimeFunc("TIME", exprList...)
|
||||
}
|
||||
|
||||
// DATETIME function creates new DateTime from time-value and zero or more time modifiers
|
||||
func DATETIME(timeValue interface{}, modifiers ...Expression) DateTimeExpression {
|
||||
exprList := getFuncExprList(timeValue, modifiers...)
|
||||
|
||||
return jet.NewTimestampFunc("DATETIME", exprList...)
|
||||
}
|
||||
|
||||
// JULIANDAY returns the number of days since noon in Greenwich on November 24, 4714 B.C
|
||||
func JULIANDAY(timeValue interface{}, modifiers ...Expression) FloatExpression {
|
||||
exprList := getFuncExprList(timeValue, modifiers...)
|
||||
return jet.NewFloatFunc("JULIANDAY", exprList...)
|
||||
}
|
||||
|
||||
// STRFTIME routine returns the date formatted according to the format string specified as the first argument.
|
||||
func STRFTIME(format StringExpression, timeValue interface{}, modifiers ...Expression) StringExpression {
|
||||
exprList := append([]Expression{format}, getFuncExprList(timeValue, modifiers...)...)
|
||||
return jet.NewStringFunc("strftime", exprList...)
|
||||
}
|
||||
|
||||
func getFuncExprList(timeValue interface{}, modifiers ...Expression) []Expression {
|
||||
return append([]Expression{getTimeValueExpression(timeValue)}, modifiers...)
|
||||
}
|
||||
|
||||
func getTimeValueExpression(timeValue interface{}) Expression {
|
||||
switch t := timeValue.(type) {
|
||||
case string:
|
||||
return String(t)
|
||||
case Expression:
|
||||
return t
|
||||
case time.Time, int64:
|
||||
return jet.Literal(t)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("jet: Invalid time value %T(%q)", timeValue, timeValue))
|
||||
}
|
||||
|
||||
// TIMESTAMP return a datetime value based on the arguments:
|
||||
func TIMESTAMP(str StringExpression) TimestampExpression {
|
||||
return jet.NewTimestampFunc("TIMESTAMP", str)
|
||||
}
|
||||
|
||||
// UNIX_TIMESTAMP returns unix timestamp
|
||||
func UNIX_TIMESTAMP(str StringExpression) TimestampExpression {
|
||||
return jet.NewTimestampFunc("UNIX_TIMESTAMP", str)
|
||||
}
|
||||
|
||||
//----------- Comparison operators ---------------//
|
||||
|
||||
// EXISTS checks for existence of the rows in subQuery
|
||||
var EXISTS = jet.EXISTS
|
||||
|
||||
// CASE create CASE operator with optional list of expressions
|
||||
var CASE = jet.CASE
|
||||
117
sqlite/insert_statement.go
Normal file
117
sqlite/insert_statement.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// InsertStatement is interface for SQL INSERT statements
|
||||
type InsertStatement interface {
|
||||
Statement
|
||||
|
||||
VALUES(value interface{}, values ...interface{}) InsertStatement
|
||||
MODEL(data interface{}) InsertStatement
|
||||
MODELS(data interface{}) InsertStatement
|
||||
QUERY(selectStatement SelectStatement) InsertStatement
|
||||
DEFAULT_VALUES() InsertStatement
|
||||
|
||||
ON_CONFLICT(indexExpressions ...jet.ColumnExpression) onConflict
|
||||
RETURNING(projections ...Projection) InsertStatement
|
||||
}
|
||||
|
||||
func newInsertStatement(table Table, columns []jet.Column) InsertStatement {
|
||||
newInsert := &insertStatementImpl{
|
||||
DefaultValues: jet.ClauseOptional{Name: "DEFAULT VALUES", InNewLine: true},
|
||||
}
|
||||
|
||||
newInsert.SerializerStatement = jet.NewStatementImpl(Dialect, jet.InsertStatementType, newInsert,
|
||||
&newInsert.Insert,
|
||||
&newInsert.ValuesQuery,
|
||||
&newInsert.OnDuplicateKey,
|
||||
&newInsert.DefaultValues,
|
||||
&newInsert.OnConflict,
|
||||
&newInsert.Returning,
|
||||
)
|
||||
|
||||
newInsert.Insert.Table = table
|
||||
newInsert.Insert.Columns = columns
|
||||
newInsert.ValuesQuery.SkipSelectWrap = true
|
||||
|
||||
return newInsert
|
||||
}
|
||||
|
||||
type insertStatementImpl struct {
|
||||
jet.SerializerStatement
|
||||
|
||||
Insert jet.ClauseInsert
|
||||
ValuesQuery jet.ClauseValuesQuery
|
||||
OnDuplicateKey onDuplicateKeyUpdateClause
|
||||
DefaultValues jet.ClauseOptional
|
||||
OnConflict onConflictClause
|
||||
Returning jet.ClauseReturning
|
||||
}
|
||||
|
||||
func (is *insertStatementImpl) VALUES(value interface{}, values ...interface{}) InsertStatement {
|
||||
is.ValuesQuery.Rows = append(is.ValuesQuery.Rows, jet.UnwindRowFromValues(value, values))
|
||||
return is
|
||||
}
|
||||
|
||||
// MODEL will insert row of values, where value for each column is extracted from filed of structure data.
|
||||
// If data is not struct or there is no field for every column selected, this method will panic.
|
||||
func (is *insertStatementImpl) MODEL(data interface{}) InsertStatement {
|
||||
is.ValuesQuery.Rows = append(is.ValuesQuery.Rows, jet.UnwindRowFromModel(is.Insert.GetColumns(), data))
|
||||
return is
|
||||
}
|
||||
|
||||
func (is *insertStatementImpl) MODELS(data interface{}) InsertStatement {
|
||||
is.ValuesQuery.Rows = append(is.ValuesQuery.Rows, jet.UnwindRowsFromModels(is.Insert.GetColumns(), data)...)
|
||||
return is
|
||||
}
|
||||
|
||||
func (is *insertStatementImpl) ON_DUPLICATE_KEY_UPDATE(assigments ...ColumnAssigment) InsertStatement {
|
||||
is.OnDuplicateKey = assigments
|
||||
return is
|
||||
}
|
||||
|
||||
func (is *insertStatementImpl) QUERY(selectStatement SelectStatement) InsertStatement {
|
||||
is.ValuesQuery.Query = selectStatement
|
||||
return is
|
||||
}
|
||||
|
||||
func (is *insertStatementImpl) DEFAULT_VALUES() InsertStatement {
|
||||
is.DefaultValues.Show = true
|
||||
return is
|
||||
}
|
||||
|
||||
func (is *insertStatementImpl) RETURNING(projections ...jet.Projection) InsertStatement {
|
||||
is.Returning.ProjectionList = projections
|
||||
return is
|
||||
}
|
||||
|
||||
type onDuplicateKeyUpdateClause []jet.ColumnAssigment
|
||||
|
||||
// Serialize for SetClause
|
||||
func (s onDuplicateKeyUpdateClause) Serialize(statementType jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) {
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
out.NewLine()
|
||||
out.WriteString("ON DUPLICATE KEY UPDATE")
|
||||
out.IncreaseIdent(24)
|
||||
|
||||
for i, assigment := range s {
|
||||
if i > 0 {
|
||||
out.WriteString(",")
|
||||
out.NewLine()
|
||||
}
|
||||
|
||||
jet.Serialize(assigment, statementType, out, jet.ShortName.WithFallTrough(options)...)
|
||||
}
|
||||
|
||||
out.DecreaseIdent(24)
|
||||
}
|
||||
|
||||
func (is *insertStatementImpl) ON_CONFLICT(indexExpressions ...jet.ColumnExpression) onConflict {
|
||||
is.OnConflict = onConflictClause{
|
||||
insertStatement: is,
|
||||
indexExpressions: indexExpressions,
|
||||
}
|
||||
return &is.OnConflict
|
||||
}
|
||||
150
sqlite/insert_statement_test.go
Normal file
150
sqlite/insert_statement_test.go
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestInvalidInsert(t *testing.T) {
|
||||
assertStatementSqlErr(t, table1.INSERT(nil).VALUES(1), "jet: nil column in columns list")
|
||||
}
|
||||
|
||||
func TestInsertNilValue(t *testing.T) {
|
||||
assertStatementSql(t, table1.INSERT(table1Col1).VALUES(nil), `
|
||||
INSERT INTO db.table1 (col1)
|
||||
VALUES (?);
|
||||
`, nil)
|
||||
}
|
||||
|
||||
func TestInsertSingleValue(t *testing.T) {
|
||||
assertStatementSql(t, table1.INSERT(table1Col1).VALUES(1), `
|
||||
INSERT INTO db.table1 (col1)
|
||||
VALUES (?);
|
||||
`, int(1))
|
||||
}
|
||||
|
||||
func TestInsertWithColumnList(t *testing.T) {
|
||||
columnList := ColumnList{table3ColInt}
|
||||
|
||||
columnList = append(columnList, table3StrCol)
|
||||
|
||||
assertStatementSql(t, table3.INSERT(columnList).VALUES(1, 3), `
|
||||
INSERT INTO db.table3 (col_int, col2)
|
||||
VALUES (?, ?);
|
||||
`, 1, 3)
|
||||
}
|
||||
|
||||
func TestInsertDate(t *testing.T) {
|
||||
date := time.Date(1999, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
|
||||
assertStatementSql(t, table1.INSERT(table1ColTimestamp).VALUES(date), `
|
||||
INSERT INTO db.table1 (col_timestamp)
|
||||
VALUES (?);
|
||||
`, date)
|
||||
}
|
||||
|
||||
func TestInsertMultipleValues(t *testing.T) {
|
||||
assertStatementSql(t, table1.INSERT(table1Col1, table1ColFloat, table1Col3).VALUES(1, 2, 3), `
|
||||
INSERT INTO db.table1 (col1, col_float, col3)
|
||||
VALUES (?, ?, ?);
|
||||
`, 1, 2, 3)
|
||||
}
|
||||
|
||||
func TestInsertMultipleRows(t *testing.T) {
|
||||
stmt := table1.INSERT(table1Col1, table1ColFloat).
|
||||
VALUES(1, 2).
|
||||
VALUES(11, 22).
|
||||
VALUES(111, 222)
|
||||
|
||||
assertStatementSql(t, stmt, `
|
||||
INSERT INTO db.table1 (col1, col_float)
|
||||
VALUES (?, ?),
|
||||
(?, ?),
|
||||
(?, ?);
|
||||
`, 1, 2, 11, 22, 111, 222)
|
||||
}
|
||||
|
||||
func TestInsertValuesFromModel(t *testing.T) {
|
||||
type Table1Model struct {
|
||||
Col1 *int
|
||||
ColFloat float64
|
||||
}
|
||||
|
||||
one := 1
|
||||
|
||||
toInsert := Table1Model{
|
||||
Col1: &one,
|
||||
ColFloat: 1.11,
|
||||
}
|
||||
|
||||
stmt := table1.INSERT(table1Col1, table1ColFloat).
|
||||
MODEL(toInsert).
|
||||
MODEL(&toInsert)
|
||||
|
||||
expectedSQL := `
|
||||
INSERT INTO db.table1 (col1, col_float)
|
||||
VALUES (?, ?),
|
||||
(?, ?);
|
||||
`
|
||||
|
||||
assertStatementSql(t, stmt, expectedSQL, int(1), float64(1.11), int(1), float64(1.11))
|
||||
}
|
||||
|
||||
func TestInsertValuesFromModelColumnMismatch(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
require.Equal(t, r, "missing struct field for column : col1")
|
||||
}()
|
||||
type Table1Model struct {
|
||||
Col1Prim int
|
||||
Col2 string
|
||||
}
|
||||
|
||||
newData := Table1Model{
|
||||
Col1Prim: 1,
|
||||
Col2: "one",
|
||||
}
|
||||
|
||||
table1.
|
||||
INSERT(table1Col1, table1ColFloat).
|
||||
MODEL(newData)
|
||||
}
|
||||
|
||||
func TestInsertFromNonStructModel(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
r := recover()
|
||||
require.Equal(t, r, "jet: data has to be a struct")
|
||||
}()
|
||||
|
||||
table2.INSERT(table2ColInt).MODEL([]int{})
|
||||
}
|
||||
|
||||
func TestInsert_ON_CONFLICT(t *testing.T) {
|
||||
stmt := table1.INSERT(table1Col1, table1ColBool).
|
||||
VALUES("one", "two").
|
||||
VALUES("1", "2").
|
||||
VALUES("theta", "beta").
|
||||
ON_CONFLICT(table1ColBool).WHERE(table1ColBool.IS_NOT_FALSE()).DO_UPDATE(
|
||||
SET(table1ColBool.SET(Bool(true)),
|
||||
table2ColInt.SET(Int(1)),
|
||||
ColumnList{table1Col1, table1ColBool}.SET(ROW(Int(2), String("two"))),
|
||||
).WHERE(table1Col1.GT(Int(2))),
|
||||
).
|
||||
RETURNING(table1Col1, table1ColBool)
|
||||
|
||||
assertStatementSql(t, stmt, `
|
||||
INSERT INTO db.table1 (col1, col_bool)
|
||||
VALUES (?, ?),
|
||||
(?, ?),
|
||||
(?, ?)
|
||||
ON CONFLICT (col_bool) WHERE col_bool IS NOT FALSE DO UPDATE
|
||||
SET col_bool = ?,
|
||||
col_int = ?,
|
||||
(col1, col_bool) = (?, ?)
|
||||
WHERE table1.col1 > ?
|
||||
RETURNING table1.col1 AS "table1.col1",
|
||||
table1.col_bool AS "table1.col_bool";
|
||||
`)
|
||||
}
|
||||
70
sqlite/literal.go
Normal file
70
sqlite/literal.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Keywords
|
||||
var (
|
||||
STAR = jet.STAR
|
||||
NULL = jet.NULL
|
||||
)
|
||||
|
||||
// Bool creates new bool literal expression
|
||||
var Bool = jet.Bool
|
||||
|
||||
// Int is constructor for 64 bit signed integer expressions literals.
|
||||
var Int = jet.Int
|
||||
|
||||
// Int8 is constructor for 8 bit signed integer expressions literals.
|
||||
var Int8 = jet.Int8
|
||||
|
||||
// Int16 is constructor for 16 bit signed integer expressions literals.
|
||||
var Int16 = jet.Int16
|
||||
|
||||
// Int32 is constructor for 32 bit signed integer expressions literals.
|
||||
var Int32 = jet.Int32
|
||||
|
||||
// Int64 is constructor for 64 bit signed integer expressions literals.
|
||||
var Int64 = jet.Int
|
||||
|
||||
// Uint8 is constructor for 8 bit unsigned integer expressions literals.
|
||||
var Uint8 = jet.Uint8
|
||||
|
||||
// Uint16 is constructor for 16 bit unsigned integer expressions literals.
|
||||
var Uint16 = jet.Uint16
|
||||
|
||||
// Uint32 is constructor for 32 bit unsigned integer expressions literals.
|
||||
var Uint32 = jet.Uint32
|
||||
|
||||
// Uint64 is constructor for 64 bit unsigned integer expressions literals.
|
||||
var Uint64 = jet.Uint64
|
||||
|
||||
// Float creates new float literal expression from float64 value
|
||||
var Float = jet.Float
|
||||
|
||||
// Decimal creates new float literal expression from string value
|
||||
var Decimal = jet.Decimal
|
||||
|
||||
// String creates new string literal expression
|
||||
var String = jet.String
|
||||
|
||||
// UUID is a helper function to create string literal expression from uuid object
|
||||
// value can be any uuid type with a String method
|
||||
var UUID = jet.UUID
|
||||
|
||||
// Date creates new date literal expression
|
||||
func Date(year int, month time.Month, day int) DateExpression {
|
||||
return DATE(jet.Date(year, month, day))
|
||||
}
|
||||
|
||||
// Time creates new time literal expression
|
||||
func Time(hour, minute, second int, nanoseconds ...time.Duration) TimeExpression {
|
||||
return TIME(jet.Time(hour, minute, second, nanoseconds...))
|
||||
}
|
||||
|
||||
// DateTime creates new datetime(timestamp) literal expression
|
||||
func DateTime(year int, month time.Month, day, hour, minute, second int, nanoseconds ...time.Duration) DateTimeExpression {
|
||||
return DATETIME(jet.Timestamp(year, month, day, hour, minute, second, nanoseconds...))
|
||||
}
|
||||
80
sqlite/literal_test.go
Normal file
80
sqlite/literal_test.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
assertSerialize(t, Bool(false), `?`, false)
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
assertSerialize(t, Int(11), `?`, int64(11))
|
||||
}
|
||||
|
||||
func TestInt8(t *testing.T) {
|
||||
val := int8(math.MinInt8)
|
||||
assertSerialize(t, Int8(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestInt16(t *testing.T) {
|
||||
val := int16(math.MinInt16)
|
||||
assertSerialize(t, Int16(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestInt32(t *testing.T) {
|
||||
val := int32(math.MinInt32)
|
||||
assertSerialize(t, Int32(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
val := int64(math.MinInt64)
|
||||
assertSerialize(t, Int64(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestUint8(t *testing.T) {
|
||||
val := uint8(math.MaxUint8)
|
||||
assertSerialize(t, Uint8(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestUint16(t *testing.T) {
|
||||
val := uint16(math.MaxUint16)
|
||||
assertSerialize(t, Uint16(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestUint32(t *testing.T) {
|
||||
val := uint32(math.MaxUint32)
|
||||
assertSerialize(t, Uint32(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestUint64(t *testing.T) {
|
||||
val := uint64(math.MaxUint64)
|
||||
assertSerialize(t, Uint64(val), `?`, val)
|
||||
}
|
||||
|
||||
func TestFloat(t *testing.T) {
|
||||
assertSerialize(t, Float(12.34), `?`, float64(12.34))
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
assertSerialize(t, String("Some text"), `?`, "Some text")
|
||||
}
|
||||
|
||||
var testTime = time.Now()
|
||||
|
||||
func TestDate(t *testing.T) {
|
||||
assertSerialize(t, Date(2014, time.January, 2), "DATE(?)", "2014-01-02")
|
||||
assertSerialize(t, DATE(testTime), "DATE(?)", testTime)
|
||||
}
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
assertSerialize(t, Time(10, 15, 30), `TIME(?)`, "10:15:30")
|
||||
assertSerialize(t, TIME(testTime), "TIME(?)", testTime)
|
||||
}
|
||||
|
||||
func TestDateTime(t *testing.T) {
|
||||
assertSerialize(t, DateTime(2010, time.March, 30, 10, 15, 30), `DATETIME(?)`, "2010-03-30 10:15:30")
|
||||
assertSerialize(t, DATETIME(testTime), `DATETIME(?)`, testTime)
|
||||
}
|
||||
84
sqlite/on_conflict_clause.go
Normal file
84
sqlite/on_conflict_clause.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
)
|
||||
|
||||
type onConflict interface {
|
||||
WHERE(indexPredicate BoolExpression) conflictTarget
|
||||
conflictTarget
|
||||
}
|
||||
|
||||
type conflictTarget interface {
|
||||
DO_NOTHING() InsertStatement
|
||||
DO_UPDATE(action conflictAction) InsertStatement
|
||||
}
|
||||
|
||||
type onConflictClause struct {
|
||||
insertStatement InsertStatement
|
||||
indexExpressions []jet.ColumnExpression
|
||||
whereClause jet.ClauseWhere
|
||||
do jet.Serializer
|
||||
}
|
||||
|
||||
func (o *onConflictClause) WHERE(indexPredicate BoolExpression) conflictTarget {
|
||||
o.whereClause.Condition = indexPredicate
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *onConflictClause) DO_NOTHING() InsertStatement {
|
||||
o.do = jet.Keyword("DO NOTHING")
|
||||
return o.insertStatement
|
||||
}
|
||||
|
||||
func (o *onConflictClause) DO_UPDATE(action conflictAction) InsertStatement {
|
||||
o.do = action
|
||||
return o.insertStatement
|
||||
}
|
||||
|
||||
func (o *onConflictClause) Serialize(statementType jet.StatementType, out *jet.SQLBuilder, options ...jet.SerializeOption) {
|
||||
if len(o.indexExpressions) == 0 && o.do == nil {
|
||||
return
|
||||
}
|
||||
|
||||
out.NewLine()
|
||||
out.WriteString("ON CONFLICT")
|
||||
if len(o.indexExpressions) > 0 {
|
||||
out.WriteString("(")
|
||||
jet.SerializeColumnExpressionNames(o.indexExpressions, statementType, out, jet.ShortName)
|
||||
out.WriteString(")")
|
||||
}
|
||||
|
||||
o.whereClause.Serialize(statementType, out, jet.SkipNewLine, jet.ShortName)
|
||||
|
||||
out.IncreaseIdent(7)
|
||||
jet.Serialize(o.do, statementType, out)
|
||||
out.DecreaseIdent(7)
|
||||
}
|
||||
|
||||
type conflictAction interface {
|
||||
jet.Serializer
|
||||
WHERE(condition BoolExpression) conflictAction
|
||||
}
|
||||
|
||||
// SET creates conflict action for ON_CONFLICT clause
|
||||
func SET(assigments ...ColumnAssigment) conflictAction {
|
||||
conflictAction := updateConflictActionImpl{}
|
||||
conflictAction.doUpdate = jet.KeywordClause{Keyword: "DO UPDATE"}
|
||||
conflictAction.Serializer = jet.NewSerializerClauseImpl(&conflictAction.doUpdate, &conflictAction.set, &conflictAction.where)
|
||||
conflictAction.set = assigments
|
||||
return &conflictAction
|
||||
}
|
||||
|
||||
type updateConflictActionImpl struct {
|
||||
jet.Serializer
|
||||
|
||||
doUpdate jet.KeywordClause
|
||||
set jet.SetClauseNew
|
||||
where jet.ClauseWhere
|
||||
}
|
||||
|
||||
func (u *updateConflictActionImpl) WHERE(condition BoolExpression) conflictAction {
|
||||
u.where.Condition = condition
|
||||
return u
|
||||
}
|
||||
9
sqlite/operators.go
Normal file
9
sqlite/operators.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// NOT returns negation of bool expression result
|
||||
var NOT = jet.NOT
|
||||
|
||||
// BIT_NOT inverts every bit in integer expression result
|
||||
var BIT_NOT = jet.BIT_NOT
|
||||
186
sqlite/select_statement.go
Normal file
186
sqlite/select_statement.go
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
)
|
||||
|
||||
// RowLock is interface for SELECT statement row lock types
|
||||
type RowLock = jet.RowLock
|
||||
|
||||
// Row lock types
|
||||
var (
|
||||
UPDATE = jet.NewRowLock("UPDATE")
|
||||
SHARE = jet.NewRowLock("SHARE")
|
||||
)
|
||||
|
||||
// Window function clauses
|
||||
var (
|
||||
PARTITION_BY = jet.PARTITION_BY
|
||||
ORDER_BY = jet.ORDER_BY
|
||||
UNBOUNDED = jet.UNBOUNDED
|
||||
CURRENT_ROW = jet.CURRENT_ROW
|
||||
)
|
||||
|
||||
// PRECEDING window frame clause
|
||||
func PRECEDING(offset interface{}) jet.FrameExtent {
|
||||
return jet.PRECEDING(toJetFrameOffset(offset))
|
||||
}
|
||||
|
||||
// FOLLOWING window frame clause
|
||||
func FOLLOWING(offset interface{}) jet.FrameExtent {
|
||||
return jet.FOLLOWING(toJetFrameOffset(offset))
|
||||
}
|
||||
|
||||
// Window is used to specify window reference from WINDOW clause
|
||||
var Window = jet.WindowName
|
||||
|
||||
// SelectStatement is interface for MySQL SELECT statement
|
||||
type SelectStatement interface {
|
||||
Statement
|
||||
jet.HasProjections
|
||||
Expression
|
||||
|
||||
DISTINCT() SelectStatement
|
||||
FROM(tables ...ReadableTable) SelectStatement
|
||||
WHERE(expression BoolExpression) SelectStatement
|
||||
GROUP_BY(groupByClauses ...GroupByClause) SelectStatement
|
||||
HAVING(boolExpression BoolExpression) SelectStatement
|
||||
WINDOW(name string) windowExpand
|
||||
ORDER_BY(orderByClauses ...OrderByClause) SelectStatement
|
||||
LIMIT(limit int64) SelectStatement
|
||||
OFFSET(offset int64) SelectStatement
|
||||
FOR(lock RowLock) SelectStatement
|
||||
LOCK_IN_SHARE_MODE() SelectStatement
|
||||
|
||||
UNION(rhs SelectStatement) setStatement
|
||||
UNION_ALL(rhs SelectStatement) setStatement
|
||||
|
||||
AsTable(alias string) SelectTable
|
||||
}
|
||||
|
||||
//SELECT creates new SelectStatement with list of projections
|
||||
func SELECT(projection Projection, projections ...Projection) SelectStatement {
|
||||
return newSelectStatement(nil, append([]Projection{projection}, projections...))
|
||||
}
|
||||
|
||||
func newSelectStatement(table ReadableTable, projections []Projection) SelectStatement {
|
||||
newSelect := &selectStatementImpl{}
|
||||
newSelect.ExpressionStatement = jet.NewExpressionStatementImpl(Dialect, jet.SelectStatementType, newSelect, &newSelect.Select,
|
||||
&newSelect.From, &newSelect.Where, &newSelect.GroupBy, &newSelect.Having, &newSelect.Window, &newSelect.OrderBy,
|
||||
&newSelect.Limit, &newSelect.Offset, &newSelect.For, &newSelect.ShareLock)
|
||||
|
||||
newSelect.Select.ProjectionList = projections
|
||||
if table != nil {
|
||||
newSelect.From.Tables = []jet.Serializer{table}
|
||||
}
|
||||
newSelect.Limit.Count = -1
|
||||
newSelect.Offset.Count = -1
|
||||
newSelect.ShareLock.Name = "LOCK IN SHARE MODE"
|
||||
newSelect.ShareLock.InNewLine = true
|
||||
|
||||
newSelect.setOperatorsImpl.parent = newSelect
|
||||
|
||||
return newSelect
|
||||
}
|
||||
|
||||
type selectStatementImpl struct {
|
||||
jet.ExpressionStatement
|
||||
setOperatorsImpl
|
||||
|
||||
Select jet.ClauseSelect
|
||||
From jet.ClauseFrom
|
||||
Where jet.ClauseWhere
|
||||
GroupBy jet.ClauseGroupBy
|
||||
Having jet.ClauseHaving
|
||||
Window jet.ClauseWindow
|
||||
OrderBy jet.ClauseOrderBy
|
||||
Limit jet.ClauseLimit
|
||||
Offset jet.ClauseOffset
|
||||
For jet.ClauseFor
|
||||
ShareLock jet.ClauseOptional
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) DISTINCT() SelectStatement {
|
||||
s.Select.Distinct = true
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement {
|
||||
s.From.Tables = nil
|
||||
for _, table := range tables {
|
||||
s.From.Tables = append(s.From.Tables, table)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) WHERE(condition BoolExpression) SelectStatement {
|
||||
s.Where.Condition = condition
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) GROUP_BY(groupByClauses ...GroupByClause) SelectStatement {
|
||||
s.GroupBy.List = groupByClauses
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) HAVING(boolExpression BoolExpression) SelectStatement {
|
||||
s.Having.Condition = boolExpression
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) WINDOW(name string) windowExpand {
|
||||
s.Window.Definitions = append(s.Window.Definitions, jet.WindowDefinition{Name: name})
|
||||
return windowExpand{selectStatement: s}
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) SelectStatement {
|
||||
s.OrderBy.List = orderByClauses
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) LIMIT(limit int64) SelectStatement {
|
||||
s.Limit.Count = limit
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) OFFSET(offset int64) SelectStatement {
|
||||
s.Offset.Count = offset
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) FOR(lock RowLock) SelectStatement {
|
||||
s.For.Lock = lock
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) LOCK_IN_SHARE_MODE() SelectStatement {
|
||||
s.ShareLock.Show = true
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *selectStatementImpl) AsTable(alias string) SelectTable {
|
||||
return newSelectTable(s, alias)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
|
||||
type windowExpand struct {
|
||||
selectStatement *selectStatementImpl
|
||||
}
|
||||
|
||||
func (w windowExpand) AS(window ...jet.Window) SelectStatement {
|
||||
if len(window) == 0 {
|
||||
return w.selectStatement
|
||||
}
|
||||
windowsDefinition := w.selectStatement.Window.Definitions
|
||||
windowsDefinition[len(windowsDefinition)-1].Window = window[0]
|
||||
return w.selectStatement
|
||||
}
|
||||
|
||||
func toJetFrameOffset(offset interface{}) jet.Serializer {
|
||||
if offset == UNBOUNDED {
|
||||
return jet.UNBOUNDED
|
||||
}
|
||||
|
||||
return jet.FixedLiteral(offset)
|
||||
}
|
||||
156
sqlite/select_statement_test.go
Normal file
156
sqlite/select_statement_test.go
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/testutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInvalidSelect(t *testing.T) {
|
||||
assertStatementSqlErr(t, SELECT(nil), "jet: Projection is nil")
|
||||
}
|
||||
|
||||
func TestSelectColumnList(t *testing.T) {
|
||||
columnList := ColumnList{table2ColInt, table2ColFloat, table3ColInt}
|
||||
|
||||
assertStatementSql(t, SELECT(columnList).FROM(table2), `
|
||||
SELECT table2.col_int AS "table2.col_int",
|
||||
table2.col_float AS "table2.col_float",
|
||||
table3.col_int AS "table3.col_int"
|
||||
FROM db.table2;
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelectLiterals(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(Int(1), Float(2.2), Bool(false)).FROM(table1), `
|
||||
SELECT ?,
|
||||
?,
|
||||
?
|
||||
FROM db.table1;
|
||||
`, int64(1), 2.2, false)
|
||||
}
|
||||
|
||||
func TestSelectDistinct(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(table1ColBool).DISTINCT().FROM(table1), `
|
||||
SELECT DISTINCT table1.col_bool AS "table1.col_bool"
|
||||
FROM db.table1;
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelectFrom(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(table1ColInt, table2ColFloat).FROM(table1), `
|
||||
SELECT table1.col_int AS "table1.col_int",
|
||||
table2.col_float AS "table2.col_float"
|
||||
FROM db.table1;
|
||||
`)
|
||||
assertStatementSql(t, SELECT(table1ColInt, table2ColFloat).FROM(table1.INNER_JOIN(table2, table1ColInt.EQ(table2ColInt))), `
|
||||
SELECT table1.col_int AS "table1.col_int",
|
||||
table2.col_float AS "table2.col_float"
|
||||
FROM db.table1
|
||||
INNER JOIN db.table2 ON (table1.col_int = table2.col_int);
|
||||
`)
|
||||
assertStatementSql(t, table1.INNER_JOIN(table2, table1ColInt.EQ(table2ColInt)).SELECT(table1ColInt, table2ColFloat), `
|
||||
SELECT table1.col_int AS "table1.col_int",
|
||||
table2.col_float AS "table2.col_float"
|
||||
FROM db.table1
|
||||
INNER JOIN db.table2 ON (table1.col_int = table2.col_int);
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelectWhere(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(table1ColInt).FROM(table1).WHERE(Bool(true)), `
|
||||
SELECT table1.col_int AS "table1.col_int"
|
||||
FROM db.table1
|
||||
WHERE ?;
|
||||
`, true)
|
||||
assertStatementSql(t, SELECT(table1ColInt).FROM(table1).WHERE(table1ColInt.GT_EQ(Int(10))), `
|
||||
SELECT table1.col_int AS "table1.col_int"
|
||||
FROM db.table1
|
||||
WHERE table1.col_int >= ?;
|
||||
`, int64(10))
|
||||
}
|
||||
|
||||
func TestSelectGroupBy(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(table2ColInt).FROM(table2).GROUP_BY(table2ColFloat), `
|
||||
SELECT table2.col_int AS "table2.col_int"
|
||||
FROM db.table2
|
||||
GROUP BY table2.col_float;
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelectHaving(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(table3ColInt).FROM(table3).HAVING(table1ColBool.EQ(Bool(true))), `
|
||||
SELECT table3.col_int AS "table3.col_int"
|
||||
FROM db.table3
|
||||
HAVING table1.col_bool = ?;
|
||||
`, true)
|
||||
}
|
||||
|
||||
func TestSelectOrderBy(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(table2ColFloat).FROM(table2).ORDER_BY(table2ColInt.DESC()), `
|
||||
SELECT table2.col_float AS "table2.col_float"
|
||||
FROM db.table2
|
||||
ORDER BY table2.col_int DESC;
|
||||
`)
|
||||
assertStatementSql(t, SELECT(table2ColFloat).FROM(table2).ORDER_BY(table2ColInt.DESC(), table2ColInt.ASC()), `
|
||||
SELECT table2.col_float AS "table2.col_float"
|
||||
FROM db.table2
|
||||
ORDER BY table2.col_int DESC, table2.col_int ASC;
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelectLimitOffset(t *testing.T) {
|
||||
assertStatementSql(t, SELECT(table2ColInt).FROM(table2).LIMIT(10), `
|
||||
SELECT table2.col_int AS "table2.col_int"
|
||||
FROM db.table2
|
||||
LIMIT ?;
|
||||
`, int64(10))
|
||||
assertStatementSql(t, SELECT(table2ColInt).FROM(table2).LIMIT(10).OFFSET(2), `
|
||||
SELECT table2.col_int AS "table2.col_int"
|
||||
FROM db.table2
|
||||
LIMIT ?
|
||||
OFFSET ?;
|
||||
`, int64(10), int64(2))
|
||||
}
|
||||
|
||||
func TestSelectLock(t *testing.T) {
|
||||
testutils.AssertStatementSql(t, SELECT(table1ColBool).FROM(table1).FOR(UPDATE()), `
|
||||
SELECT table1.col_bool AS "table1.col_bool"
|
||||
FROM db.table1
|
||||
FOR UPDATE;
|
||||
`)
|
||||
testutils.AssertStatementSql(t, SELECT(table1ColBool).FROM(table1).FOR(SHARE().NOWAIT()), `
|
||||
SELECT table1.col_bool AS "table1.col_bool"
|
||||
FROM db.table1
|
||||
FOR SHARE NOWAIT;
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelect_LOCK_IN_SHARE_MODE(t *testing.T) {
|
||||
testutils.AssertStatementSql(t, SELECT(table1ColBool).FROM(table1).LOCK_IN_SHARE_MODE(), `
|
||||
SELECT table1.col_bool AS "table1.col_bool"
|
||||
FROM db.table1
|
||||
LOCK IN SHARE MODE;
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSelect_NOT_EXISTS(t *testing.T) {
|
||||
testutils.AssertStatementSql(t,
|
||||
SELECT(table1ColInt).
|
||||
FROM(table1).
|
||||
WHERE(
|
||||
NOT(EXISTS(
|
||||
SELECT(table2ColInt).
|
||||
FROM(table2).
|
||||
WHERE(
|
||||
table1ColInt.EQ(table2ColInt),
|
||||
),
|
||||
))), `
|
||||
SELECT table1.col_int AS "table1.col_int"
|
||||
FROM db.table1
|
||||
WHERE (NOT (EXISTS (
|
||||
SELECT table2.col_int AS "table2.col_int"
|
||||
FROM db.table2
|
||||
WHERE table1.col_int = table2.col_int
|
||||
)));
|
||||
`)
|
||||
}
|
||||
24
sqlite/select_table.go
Normal file
24
sqlite/select_table.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// SelectTable is interface for MySQL sub-queries
|
||||
type SelectTable interface {
|
||||
readableTable
|
||||
jet.SelectTable
|
||||
}
|
||||
|
||||
type selectTableImpl struct {
|
||||
jet.SelectTable
|
||||
readableTableInterfaceImpl
|
||||
}
|
||||
|
||||
func newSelectTable(selectStmt jet.SerializerStatement, alias string) SelectTable {
|
||||
subQuery := &selectTableImpl{
|
||||
SelectTable: jet.NewSelectTable(selectStmt, alias),
|
||||
}
|
||||
|
||||
subQuery.readableTableInterfaceImpl.parent = subQuery
|
||||
|
||||
return subQuery
|
||||
}
|
||||
99
sqlite/set_statement.go
Normal file
99
sqlite/set_statement.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// UNION effectively appends the result of sub-queries(select statements) into single query.
|
||||
// It eliminates duplicate rows from its result.
|
||||
func UNION(lhs, rhs jet.SerializerStatement, selects ...jet.SerializerStatement) setStatement {
|
||||
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 jet.SerializerStatement, selects ...jet.SerializerStatement) setStatement {
|
||||
return newSetStatementImpl(union, true, toSelectList(lhs, rhs, selects...))
|
||||
}
|
||||
|
||||
type setStatement interface {
|
||||
setOperators
|
||||
|
||||
ORDER_BY(orderByClauses ...OrderByClause) setStatement
|
||||
|
||||
LIMIT(limit int64) setStatement
|
||||
OFFSET(offset int64) setStatement
|
||||
|
||||
AsTable(alias string) SelectTable
|
||||
}
|
||||
|
||||
type setOperators interface {
|
||||
jet.Statement
|
||||
jet.HasProjections
|
||||
jet.Expression
|
||||
|
||||
UNION(rhs SelectStatement) setStatement
|
||||
UNION_ALL(rhs SelectStatement) setStatement
|
||||
}
|
||||
|
||||
type setOperatorsImpl struct {
|
||||
parent setOperators
|
||||
}
|
||||
|
||||
func (s *setOperatorsImpl) UNION(rhs SelectStatement) setStatement {
|
||||
return UNION(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *setOperatorsImpl) UNION_ALL(rhs SelectStatement) setStatement {
|
||||
return UNION_ALL(s.parent, rhs)
|
||||
}
|
||||
|
||||
type setStatementImpl struct {
|
||||
jet.ExpressionStatement
|
||||
|
||||
setOperatorsImpl
|
||||
|
||||
setOperator jet.ClauseSetStmtOperator
|
||||
}
|
||||
|
||||
func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStatement) setStatement {
|
||||
newSetStatement := &setStatementImpl{}
|
||||
newSetStatement.ExpressionStatement = jet.NewExpressionStatementImpl(Dialect, jet.SetStatementType, newSetStatement,
|
||||
&newSetStatement.setOperator)
|
||||
|
||||
newSetStatement.setOperator.Operator = operator
|
||||
newSetStatement.setOperator.All = all
|
||||
newSetStatement.setOperator.Selects = selects
|
||||
newSetStatement.setOperator.Limit.Count = -1
|
||||
newSetStatement.setOperator.Offset.Count = -1
|
||||
newSetStatement.setOperator.SkipSelectWrap = true
|
||||
|
||||
newSetStatement.setOperatorsImpl.parent = newSetStatement
|
||||
|
||||
return newSetStatement
|
||||
}
|
||||
|
||||
func (s *setStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) setStatement {
|
||||
s.setOperator.OrderBy.List = orderByClauses
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *setStatementImpl) LIMIT(limit int64) setStatement {
|
||||
s.setOperator.Limit.Count = limit
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *setStatementImpl) OFFSET(offset int64) setStatement {
|
||||
s.setOperator.Offset.Count = offset
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *setStatementImpl) AsTable(alias string) SelectTable {
|
||||
return newSelectTable(s, alias)
|
||||
}
|
||||
|
||||
const (
|
||||
union = "UNION"
|
||||
)
|
||||
|
||||
func toSelectList(lhs, rhs jet.SerializerStatement, selects ...jet.SerializerStatement) []jet.SerializerStatement {
|
||||
return append([]jet.SerializerStatement{lhs, rhs}, selects...)
|
||||
}
|
||||
31
sqlite/set_statement_test.go
Normal file
31
sqlite/set_statement_test.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSelectSets(t *testing.T) {
|
||||
select1 := SELECT(table1ColBool).FROM(table1)
|
||||
select2 := SELECT(table2ColBool).FROM(table2)
|
||||
|
||||
assertStatementSql(t, select1.UNION(select2), `
|
||||
|
||||
SELECT table1.col_bool AS "table1.col_bool"
|
||||
FROM db.table1
|
||||
|
||||
UNION
|
||||
|
||||
SELECT table2.col_bool AS "table2.col_bool"
|
||||
FROM db.table2;
|
||||
`)
|
||||
assertStatementSql(t, select1.UNION_ALL(select2), `
|
||||
|
||||
SELECT table1.col_bool AS "table1.col_bool"
|
||||
FROM db.table1
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT table2.col_bool AS "table2.col_bool"
|
||||
FROM db.table2;
|
||||
`)
|
||||
}
|
||||
8
sqlite/statement.go
Normal file
8
sqlite/statement.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// RawStatement creates new sql statements from raw query and optional map of named arguments
|
||||
func RawStatement(rawQuery string, namedArguments ...RawArgs) Statement {
|
||||
return jet.RawStatement(Dialect, rawQuery, namedArguments...)
|
||||
}
|
||||
122
sqlite/table.go
Normal file
122
sqlite/table.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// Table is interface for MySQL tables
|
||||
type Table interface {
|
||||
jet.SerializerTable
|
||||
readableTable
|
||||
|
||||
INSERT(columns ...jet.Column) InsertStatement
|
||||
UPDATE(columns ...jet.Column) UpdateStatement
|
||||
DELETE() DeleteStatement
|
||||
}
|
||||
|
||||
type readableTable interface {
|
||||
// Generates a select query on the current tableName.
|
||||
SELECT(projection Projection, projections ...Projection) SelectStatement
|
||||
|
||||
// Creates a inner join tableName Expression using onCondition.
|
||||
INNER_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable
|
||||
|
||||
// Creates a left join tableName Expression using onCondition.
|
||||
LEFT_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable
|
||||
|
||||
// Creates a right join tableName Expression using onCondition.
|
||||
RIGHT_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable
|
||||
|
||||
// Creates a full join tableName Expression using onCondition.
|
||||
FULL_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable
|
||||
|
||||
// Creates a cross join tableName Expression using onCondition.
|
||||
CROSS_JOIN(table ReadableTable) joinSelectUpdateTable
|
||||
}
|
||||
|
||||
type joinSelectUpdateTable interface {
|
||||
ReadableTable
|
||||
UPDATE(columns ...jet.Column) UpdateStatement
|
||||
}
|
||||
|
||||
// ReadableTable interface
|
||||
type ReadableTable interface {
|
||||
readableTable
|
||||
jet.Serializer
|
||||
}
|
||||
|
||||
type readableTableInterfaceImpl struct {
|
||||
parent ReadableTable
|
||||
}
|
||||
|
||||
// Generates a select query on the current tableName.
|
||||
func (r readableTableInterfaceImpl) SELECT(projection1 Projection, projections ...Projection) SelectStatement {
|
||||
return newSelectStatement(r.parent, append([]Projection{projection1}, projections...))
|
||||
}
|
||||
|
||||
// Creates a inner join tableName Expression using onCondition.
|
||||
func (r readableTableInterfaceImpl) INNER_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable {
|
||||
return newJoinTable(r.parent, table, jet.InnerJoin, onCondition)
|
||||
}
|
||||
|
||||
// Creates a left join tableName Expression using onCondition.
|
||||
func (r readableTableInterfaceImpl) LEFT_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable {
|
||||
return newJoinTable(r.parent, table, jet.LeftJoin, onCondition)
|
||||
}
|
||||
|
||||
// Creates a right join tableName Expression using onCondition.
|
||||
func (r readableTableInterfaceImpl) RIGHT_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable {
|
||||
return newJoinTable(r.parent, table, jet.RightJoin, onCondition)
|
||||
}
|
||||
|
||||
func (r readableTableInterfaceImpl) FULL_JOIN(table ReadableTable, onCondition BoolExpression) joinSelectUpdateTable {
|
||||
return newJoinTable(r.parent, table, jet.FullJoin, onCondition)
|
||||
}
|
||||
|
||||
func (r readableTableInterfaceImpl) CROSS_JOIN(table ReadableTable) joinSelectUpdateTable {
|
||||
return newJoinTable(r.parent, table, jet.CrossJoin, nil)
|
||||
}
|
||||
|
||||
// NewTable creates new table with schema Name, table Name and list of columns
|
||||
func NewTable(schemaName, name, alias string, columns ...jet.ColumnExpression) Table {
|
||||
t := &tableImpl{
|
||||
SerializerTable: jet.NewTable(schemaName, name, alias, columns...),
|
||||
}
|
||||
|
||||
t.readableTableInterfaceImpl.parent = t
|
||||
t.parent = t
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
type tableImpl struct {
|
||||
jet.SerializerTable
|
||||
readableTableInterfaceImpl
|
||||
parent Table
|
||||
}
|
||||
|
||||
func (t *tableImpl) INSERT(columns ...jet.Column) InsertStatement {
|
||||
return newInsertStatement(t.parent, jet.UnwidColumnList(columns))
|
||||
}
|
||||
|
||||
func (t *tableImpl) UPDATE(columns ...jet.Column) UpdateStatement {
|
||||
return newUpdateStatement(t.parent, jet.UnwidColumnList(columns))
|
||||
}
|
||||
|
||||
func (t *tableImpl) DELETE() DeleteStatement {
|
||||
return newDeleteStatement(t.parent)
|
||||
}
|
||||
|
||||
type joinTable struct {
|
||||
tableImpl
|
||||
jet.JoinTable
|
||||
}
|
||||
|
||||
func newJoinTable(lhs jet.Serializer, rhs jet.Serializer, joinType jet.JoinType, onCondition BoolExpression) Table {
|
||||
newJoinTable := &joinTable{
|
||||
JoinTable: jet.NewJoinTable(lhs, rhs, joinType, onCondition),
|
||||
}
|
||||
|
||||
newJoinTable.readableTableInterfaceImpl.parent = newJoinTable
|
||||
newJoinTable.parent = newJoinTable
|
||||
|
||||
return newJoinTable
|
||||
}
|
||||
101
sqlite/table_test.go
Normal file
101
sqlite/table_test.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJoinNilInputs(t *testing.T) {
|
||||
assertSerializeErr(t, table2.INNER_JOIN(nil, table1ColBool.EQ(table2ColBool)),
|
||||
"jet: right hand side of join operation is nil table")
|
||||
assertSerializeErr(t, table2.INNER_JOIN(table1, nil),
|
||||
"jet: join condition is nil")
|
||||
}
|
||||
|
||||
func TestINNER_JOIN(t *testing.T) {
|
||||
assertSerialize(t, table1.
|
||||
INNER_JOIN(table2, table1ColInt.EQ(table2ColInt)),
|
||||
`db.table1
|
||||
INNER JOIN db.table2 ON (table1.col_int = table2.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
INNER_JOIN(table2, table1ColInt.EQ(table2ColInt)).
|
||||
INNER_JOIN(table3, table1ColInt.EQ(table3ColInt)),
|
||||
`db.table1
|
||||
INNER JOIN db.table2 ON (table1.col_int = table2.col_int)
|
||||
INNER JOIN db.table3 ON (table1.col_int = table3.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
INNER_JOIN(table2, table1ColInt.EQ(Int(1))).
|
||||
INNER_JOIN(table3, table1ColInt.EQ(Int(2))),
|
||||
`db.table1
|
||||
INNER JOIN db.table2 ON (table1.col_int = ?)
|
||||
INNER JOIN db.table3 ON (table1.col_int = ?)`, int64(1), int64(2))
|
||||
}
|
||||
|
||||
func TestLEFT_JOIN(t *testing.T) {
|
||||
assertSerialize(t, table1.
|
||||
LEFT_JOIN(table2, table1ColInt.EQ(table2ColInt)),
|
||||
`db.table1
|
||||
LEFT JOIN db.table2 ON (table1.col_int = table2.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
LEFT_JOIN(table2, table1ColInt.EQ(table2ColInt)).
|
||||
LEFT_JOIN(table3, table1ColInt.EQ(table3ColInt)),
|
||||
`db.table1
|
||||
LEFT JOIN db.table2 ON (table1.col_int = table2.col_int)
|
||||
LEFT JOIN db.table3 ON (table1.col_int = table3.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
LEFT_JOIN(table2, table1ColInt.EQ(Int(1))).
|
||||
LEFT_JOIN(table3, table1ColInt.EQ(Int(2))),
|
||||
`db.table1
|
||||
LEFT JOIN db.table2 ON (table1.col_int = ?)
|
||||
LEFT JOIN db.table3 ON (table1.col_int = ?)`, int64(1), int64(2))
|
||||
}
|
||||
|
||||
func TestRIGHT_JOIN(t *testing.T) {
|
||||
assertSerialize(t, table1.
|
||||
RIGHT_JOIN(table2, table1ColInt.EQ(table2ColInt)),
|
||||
`db.table1
|
||||
RIGHT JOIN db.table2 ON (table1.col_int = table2.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
RIGHT_JOIN(table2, table1ColInt.EQ(table2ColInt)).
|
||||
RIGHT_JOIN(table3, table1ColInt.EQ(table3ColInt)),
|
||||
`db.table1
|
||||
RIGHT JOIN db.table2 ON (table1.col_int = table2.col_int)
|
||||
RIGHT JOIN db.table3 ON (table1.col_int = table3.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
RIGHT_JOIN(table2, table1ColInt.EQ(Int(1))).
|
||||
RIGHT_JOIN(table3, table1ColInt.EQ(Int(2))),
|
||||
`db.table1
|
||||
RIGHT JOIN db.table2 ON (table1.col_int = ?)
|
||||
RIGHT JOIN db.table3 ON (table1.col_int = ?)`, int64(1), int64(2))
|
||||
}
|
||||
|
||||
func TestFULL_JOIN(t *testing.T) {
|
||||
assertSerialize(t, table1.
|
||||
FULL_JOIN(table2, table1ColInt.EQ(table2ColInt)),
|
||||
`db.table1
|
||||
FULL JOIN db.table2 ON (table1.col_int = table2.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
FULL_JOIN(table2, table1ColInt.EQ(table2ColInt)).
|
||||
FULL_JOIN(table3, table1ColInt.EQ(table3ColInt)),
|
||||
`db.table1
|
||||
FULL JOIN db.table2 ON (table1.col_int = table2.col_int)
|
||||
FULL JOIN db.table3 ON (table1.col_int = table3.col_int)`)
|
||||
assertSerialize(t, table1.
|
||||
FULL_JOIN(table2, table1ColInt.EQ(Int(1))).
|
||||
FULL_JOIN(table3, table1ColInt.EQ(Int(2))),
|
||||
`db.table1
|
||||
FULL JOIN db.table2 ON (table1.col_int = ?)
|
||||
FULL JOIN db.table3 ON (table1.col_int = ?)`, int64(1), int64(2))
|
||||
}
|
||||
|
||||
func TestCROSS_JOIN(t *testing.T) {
|
||||
assertSerialize(t, table1.
|
||||
CROSS_JOIN(table2),
|
||||
`db.table1
|
||||
CROSS JOIN db.table2`)
|
||||
assertSerialize(t, table1.
|
||||
CROSS_JOIN(table2).
|
||||
CROSS_JOIN(table3),
|
||||
`db.table1
|
||||
CROSS JOIN db.table2
|
||||
CROSS JOIN db.table3`)
|
||||
}
|
||||
27
sqlite/types.go
Normal file
27
sqlite/types.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// Statement is common interface for all statements(SELECT, INSERT, UPDATE, DELETE, LOCK)
|
||||
type Statement = jet.Statement
|
||||
|
||||
// Projection is interface for all projection types. Types that can be part of, for instance SELECT clause.
|
||||
type Projection = jet.Projection
|
||||
|
||||
// ProjectionList can be used to create conditional constructed projection list.
|
||||
type ProjectionList = jet.ProjectionList
|
||||
|
||||
// ColumnAssigment is interface wrapper around column assigment
|
||||
type ColumnAssigment = jet.ColumnAssigment
|
||||
|
||||
// PrintableStatement is a statement which sql query can be logged
|
||||
type PrintableStatement = jet.PrintableStatement
|
||||
|
||||
// OrderByClause is the combination of an expression and the wanted ordering to use as input for ORDER BY.
|
||||
type OrderByClause = jet.OrderByClause
|
||||
|
||||
// GroupByClause interface to use as input for GROUP_BY
|
||||
type GroupByClause = jet.GroupByClause
|
||||
|
||||
// SetLogger sets automatic statement logging
|
||||
var SetLogger = jet.SetLoggerFunc
|
||||
70
sqlite/update_statement.go
Normal file
70
sqlite/update_statement.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// UpdateStatement is interface of SQL UPDATE statement
|
||||
type UpdateStatement interface {
|
||||
jet.Statement
|
||||
|
||||
SET(value interface{}, values ...interface{}) UpdateStatement
|
||||
MODEL(data interface{}) UpdateStatement
|
||||
|
||||
WHERE(expression BoolExpression) UpdateStatement
|
||||
RETURNING(projections ...jet.Projection) UpdateStatement
|
||||
}
|
||||
|
||||
type updateStatementImpl struct {
|
||||
jet.SerializerStatement
|
||||
|
||||
Update jet.ClauseUpdate
|
||||
Set jet.SetClause
|
||||
SetNew jet.SetClauseNew
|
||||
Where jet.ClauseWhere
|
||||
Returning jet.ClauseReturning
|
||||
}
|
||||
|
||||
func newUpdateStatement(table Table, columns []jet.Column) UpdateStatement {
|
||||
update := &updateStatementImpl{}
|
||||
update.SerializerStatement = jet.NewStatementImpl(Dialect, jet.UpdateStatementType, update,
|
||||
&update.Update,
|
||||
&update.Set,
|
||||
&update.SetNew,
|
||||
&update.Where,
|
||||
&update.Returning)
|
||||
|
||||
update.Update.Table = table
|
||||
update.Set.Columns = columns
|
||||
update.Where.Mandatory = true
|
||||
|
||||
return update
|
||||
}
|
||||
|
||||
func (u *updateStatementImpl) SET(value interface{}, values ...interface{}) UpdateStatement {
|
||||
columnAssigment, isColumnAssigment := value.(ColumnAssigment)
|
||||
|
||||
if isColumnAssigment {
|
||||
u.SetNew = []ColumnAssigment{columnAssigment}
|
||||
for _, value := range values {
|
||||
u.SetNew = append(u.SetNew, value.(ColumnAssigment))
|
||||
}
|
||||
} else {
|
||||
u.Set.Values = jet.UnwindRowFromValues(value, values)
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *updateStatementImpl) MODEL(data interface{}) UpdateStatement {
|
||||
u.Set.Values = jet.UnwindRowFromModel(u.Set.Columns, data)
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *updateStatementImpl) WHERE(expression BoolExpression) UpdateStatement {
|
||||
u.Where.Condition = expression
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *updateStatementImpl) RETURNING(projections ...jet.Projection) UpdateStatement {
|
||||
u.Returning.ProjectionList = projections
|
||||
return u
|
||||
}
|
||||
82
sqlite/update_statement_test.go
Normal file
82
sqlite/update_statement_test.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpdateWithOneValue(t *testing.T) {
|
||||
expectedSQL := `
|
||||
UPDATE db.table1
|
||||
SET col_int = ?
|
||||
WHERE table1.col_int >= ?;
|
||||
`
|
||||
stmt := table1.UPDATE(table1ColInt).
|
||||
SET(1).
|
||||
WHERE(table1ColInt.GT_EQ(Int(33)))
|
||||
|
||||
fmt.Println(stmt.Sql())
|
||||
|
||||
assertStatementSql(t, stmt, expectedSQL, 1, int64(33))
|
||||
}
|
||||
|
||||
func TestUpdateWithValues(t *testing.T) {
|
||||
expectedSQL := `
|
||||
UPDATE db.table1
|
||||
SET col_int = ?,
|
||||
col_float = ?
|
||||
WHERE table1.col_int >= ?;
|
||||
`
|
||||
stmt := table1.UPDATE(table1ColInt, table1ColFloat).
|
||||
SET(1, 22.2).
|
||||
WHERE(table1ColInt.GT_EQ(Int(33)))
|
||||
|
||||
fmt.Println(stmt.Sql())
|
||||
|
||||
assertStatementSql(t, stmt, expectedSQL, 1, 22.2, int64(33))
|
||||
}
|
||||
|
||||
func TestUpdateOneColumnWithSelect(t *testing.T) {
|
||||
expectedSQL := `
|
||||
UPDATE db.table1
|
||||
SET col_float = (
|
||||
SELECT table1.col_float AS "table1.col_float"
|
||||
FROM db.table1
|
||||
)
|
||||
WHERE table1.col1 = ?;
|
||||
`
|
||||
stmt := table1.
|
||||
UPDATE(table1ColFloat).
|
||||
SET(
|
||||
table1.SELECT(table1ColFloat),
|
||||
).
|
||||
WHERE(table1Col1.EQ(Int(2)))
|
||||
|
||||
assertStatementSql(t, stmt, expectedSQL, int64(2))
|
||||
}
|
||||
|
||||
func TestUpdateReservedWorldColumn(t *testing.T) {
|
||||
type table struct {
|
||||
Load string
|
||||
}
|
||||
|
||||
loadColumn := StringColumn("Load")
|
||||
assertStatementSql(t,
|
||||
table1.UPDATE(loadColumn).
|
||||
MODEL(
|
||||
table{
|
||||
Load: "foo",
|
||||
},
|
||||
).
|
||||
WHERE(loadColumn.EQ(String("bar"))), strings.Replace(`
|
||||
UPDATE db.table1
|
||||
SET ''Load'' = ?
|
||||
WHERE ''Load'' = ?;
|
||||
`, "''", "`", -1), "foo", "bar")
|
||||
}
|
||||
|
||||
func TestInvalidInputs(t *testing.T) {
|
||||
assertStatementSqlErr(t, table1.UPDATE(table1ColInt).SET(1), "jet: WHERE clause not set")
|
||||
assertStatementSqlErr(t, table1.UPDATE(nil).SET(1), "jet: nil column in columns list for SET clause")
|
||||
}
|
||||
55
sqlite/utils_test.go
Normal file
55
sqlite/utils_test.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
"github.com/go-jet/jet/v2/internal/testutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var table1Col1 = IntegerColumn("col1")
|
||||
var table1ColBool = BoolColumn("col_bool")
|
||||
var table1ColInt = IntegerColumn("col_int")
|
||||
var table1ColFloat = FloatColumn("col_float")
|
||||
var table1ColString = StringColumn("col_string")
|
||||
var table1Col3 = IntegerColumn("col3")
|
||||
var table1ColTimestamp = TimestampColumn("col_timestamp")
|
||||
var table1ColDate = DateColumn("col_date")
|
||||
var table1ColTime = TimeColumn("col_time")
|
||||
|
||||
var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1ColString, table1Col3, table1ColBool, table1ColDate, table1ColTimestamp, table1ColTime)
|
||||
|
||||
var table2Col3 = IntegerColumn("col3")
|
||||
var table2Col4 = IntegerColumn("col4")
|
||||
var table2ColInt = IntegerColumn("col_int")
|
||||
var table2ColFloat = FloatColumn("col_float")
|
||||
var table2ColStr = StringColumn("col_str")
|
||||
var table2ColBool = BoolColumn("col_bool")
|
||||
var table2ColTimestamp = TimestampColumn("col_timestamp")
|
||||
var table2ColDate = DateColumn("col_date")
|
||||
|
||||
var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColDate, table2ColTimestamp)
|
||||
|
||||
var table3Col1 = IntegerColumn("col1")
|
||||
var table3ColInt = IntegerColumn("col_int")
|
||||
var table3StrCol = StringColumn("col2")
|
||||
var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol)
|
||||
|
||||
func assertSerialize(t *testing.T, clause jet.Serializer, query string, args ...interface{}) {
|
||||
testutils.AssertSerialize(t, Dialect, clause, query, args...)
|
||||
}
|
||||
|
||||
func assertDebugSerialize(t *testing.T, clause jet.Serializer, query string, args ...interface{}) {
|
||||
testutils.AssertDebugSerialize(t, Dialect, clause, query, args...)
|
||||
}
|
||||
|
||||
func assertSerializeErr(t *testing.T, clause jet.Serializer, errString string) {
|
||||
testutils.AssertSerializeErr(t, Dialect, clause, errString)
|
||||
}
|
||||
|
||||
func assertProjectionSerialize(t *testing.T, projection jet.Projection, query string, args ...interface{}) {
|
||||
testutils.AssertProjectionSerialize(t, Dialect, projection, query, args...)
|
||||
}
|
||||
|
||||
var assertPanicErr = testutils.AssertPanicErr
|
||||
var assertStatementSql = testutils.AssertStatementSql
|
||||
var assertStatementSqlErr = testutils.AssertStatementSqlErr
|
||||
26
sqlite/with_statement.go
Normal file
26
sqlite/with_statement.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package sqlite
|
||||
|
||||
import "github.com/go-jet/jet/v2/internal/jet"
|
||||
|
||||
// CommonTableExpression contains information about a CTE.
|
||||
type CommonTableExpression struct {
|
||||
readableTableInterfaceImpl
|
||||
jet.CommonTableExpression
|
||||
}
|
||||
|
||||
// WITH function creates new WITH statement from list of common table expressions
|
||||
func WITH(cte ...jet.CommonTableExpressionDefinition) func(statement jet.Statement) Statement {
|
||||
return jet.WITH(Dialect, cte...)
|
||||
}
|
||||
|
||||
// CTE creates new named CommonTableExpression
|
||||
func CTE(name string) CommonTableExpression {
|
||||
cte := CommonTableExpression{
|
||||
readableTableInterfaceImpl: readableTableInterfaceImpl{},
|
||||
CommonTableExpression: jet.CTE(name),
|
||||
}
|
||||
|
||||
cte.parent = &cte
|
||||
|
||||
return cte
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue