Add SQLBuilder support for SQLite databases.

This commit is contained in:
go-jet 2021-10-21 13:39:24 +02:00
parent d197956271
commit e8f4c2b31b
50 changed files with 5851 additions and 75 deletions

View file

@ -0,0 +1,912 @@
package sqlite
import (
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/view"
"github.com/go-jet/jet/v2/tests/testdata/results/common"
"github.com/google/uuid"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/require"
"strings"
"testing"
"time"
)
func TestAllTypes(t *testing.T) {
dest := []model.AllTypes{}
err := SELECT(AllTypes.AllColumns).
FROM(AllTypes).
Query(sampleDB, &dest)
require.NoError(t, err)
testutils.AssertJSON(t, dest, allTypesJSON)
}
var allTypesJSON = `
[
{
"Boolean": false,
"BooleanPtr": true,
"TinyInt": -3,
"TinyIntPtr": 3,
"SmallInt": 14,
"SmallIntPtr": 14,
"MediumInt": -150,
"MediumIntPtr": 150,
"Integer": -1600,
"IntegerPtr": 1600,
"BigInt": 5000,
"BigIntPtr": 50000,
"Decimal": 1.11,
"DecimalPtr": 1.01,
"Numeric": 2.22,
"NumericPtr": 2.02,
"Float": 3.33,
"FloatPtr": 3.03,
"Double": 4.44,
"DoublePtr": 4.04,
"Real": 5.55,
"RealPtr": 5.05,
"Time": "0000-01-01T10:11:12.33Z",
"TimePtr": "0000-01-01T10:11:12.123456Z",
"Date": "2008-07-04T00:00:00Z",
"DatePtr": "2008-07-04T00:00:00Z",
"DateTime": "2011-12-18T13:17:17Z",
"DateTimePtr": "2011-12-18T13:17:17Z",
"Timestamp": "2007-12-31T23:00:01Z",
"TimestampPtr": "2007-12-31T23:00:01Z",
"Char": "char1",
"CharPtr": "char-ptr",
"VarChar": "varchar",
"VarCharPtr": "varchar-ptr",
"Text": "text",
"TextPtr": "text-ptr",
"Blob": "YmxvYjE=",
"BlobPtr": "YmxvYi1wdHI="
},
{
"Boolean": false,
"BooleanPtr": null,
"TinyInt": -3,
"TinyIntPtr": null,
"SmallInt": 14,
"SmallIntPtr": null,
"MediumInt": -150,
"MediumIntPtr": null,
"Integer": -1600,
"IntegerPtr": null,
"BigInt": 5000,
"BigIntPtr": null,
"Decimal": 1.11,
"DecimalPtr": null,
"Numeric": 2.22,
"NumericPtr": null,
"Float": 3.33,
"FloatPtr": null,
"Double": 4.44,
"DoublePtr": null,
"Real": 5.55,
"RealPtr": null,
"Time": "0000-01-01T10:11:12.33Z",
"TimePtr": null,
"Date": "2008-07-04T00:00:00Z",
"DatePtr": null,
"DateTime": "2011-12-18T13:17:17Z",
"DateTimePtr": null,
"Timestamp": "2007-12-31T23:00:01Z",
"TimestampPtr": null,
"Char": "char2",
"CharPtr": null,
"VarChar": "varchar",
"VarCharPtr": null,
"Text": "text",
"TextPtr": null,
"Blob": "YmxvYjI=",
"BlobPtr": null
}
]
`
func TestAllTypesViewSelect(t *testing.T) {
var dest []model.AllTypesView
stmt := SELECT(view.AllTypesView.AllColumns).
FROM(view.AllTypesView)
err := stmt.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 2)
testutils.AssertJSON(t, dest, allTypesJSON)
}
func TestAllTypesInsert(t *testing.T) {
tx := beginSampleDBTx(t)
stmt := AllTypes.INSERT(AllTypes.AllColumns).
MODEL(toInsert).
RETURNING(AllTypes.AllColumns)
var inserted model.AllTypes
err := stmt.Query(tx, &inserted)
require.NoError(t, err)
testutils.AssertDeepEqual(t, toInsert, inserted, testutils.UnixTimeComparer)
var dest model.AllTypes
err = AllTypes.SELECT(AllTypes.AllColumns).
WHERE(AllTypes.BigInt.EQ(Int(toInsert.BigInt))).
Query(tx, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest, toInsert, testutils.UnixTimeComparer)
err = tx.Rollback()
require.NoError(t, err)
}
var toInsert = model.AllTypes{
Boolean: false,
BooleanPtr: testutils.BoolPtr(true),
TinyInt: 1,
SmallInt: 3,
MediumInt: 5,
Integer: 7,
BigInt: 9,
TinyIntPtr: testutils.Int8Ptr(11),
SmallIntPtr: testutils.Int16Ptr(33),
MediumIntPtr: testutils.Int32Ptr(55),
IntegerPtr: testutils.Int32Ptr(77),
BigIntPtr: testutils.Int64Ptr(99),
Decimal: 11.22,
DecimalPtr: testutils.Float64Ptr(33.44),
Numeric: 55.66,
NumericPtr: testutils.Float64Ptr(77.88),
Float: 99.00,
FloatPtr: testutils.Float64Ptr(11.22),
Double: 33.44,
DoublePtr: testutils.Float64Ptr(55.66),
Real: 77.88,
RealPtr: testutils.Float32Ptr(99.00),
Time: time.Date(1, 1, 1, 1, 1, 1, 10, time.UTC),
TimePtr: testutils.TimePtr(time.Date(2, 2, 2, 2, 2, 2, 200, time.UTC)),
Date: time.Now(),
DatePtr: testutils.TimePtr(time.Now()),
DateTime: time.Now(),
DateTimePtr: testutils.TimePtr(time.Now()),
Timestamp: time.Now(),
TimestampPtr: testutils.TimePtr(time.Now()),
Char: "abcd",
CharPtr: testutils.StringPtr("absd"),
VarChar: "abcd",
VarCharPtr: testutils.StringPtr("absd"),
Blob: []byte("large file"),
BlobPtr: testutils.ByteArrayPtr([]byte("very large file")),
Text: "some text",
TextPtr: testutils.StringPtr("text"),
}
func TestUUID(t *testing.T) {
query := SELECT(
//Raw("uuid()").AS("uuid"),
String("dc8daae3-b83b-11e9-8eb4-98ded00c39c6").AS("str_uuid"),
)
var dest struct {
UUID uuid.UUID
StrUUID *uuid.UUID
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, dest.StrUUID.String(), "dc8daae3-b83b-11e9-8eb4-98ded00c39c6")
requireLogged(t, query)
}
func TestExpressionOperators(t *testing.T) {
query := SELECT(
AllTypes.Integer.IS_NULL().AS("result.is_null"),
AllTypes.DatePtr.IS_NOT_NULL().AS("result.is_not_null"),
AllTypes.SmallIntPtr.IN(Int(11), Int(22)).AS("result.in"),
AllTypes.SmallIntPtr.IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.in_select"),
Raw("length(121232459)").AS("result.raw"),
Raw(":first + COALESCE(all_types.small_int_ptr, 0) + :second", RawArgs{":first": 78, ":second": 56}).
AS("result.raw_arg"),
Raw("#1 + all_types.integer + #2 + #1 + #3 + #4", RawArgs{"#1": 11, "#2": 22, "#3": 33, "#4": 44}).
AS("result.raw_arg2"),
AllTypes.SmallIntPtr.NOT_IN(Int(11), Int(22), NULL).AS("result.not_in"),
AllTypes.SmallIntPtr.NOT_IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"),
).FROM(
AllTypes,
).LIMIT(2)
testutils.AssertStatementSql(t, query, strings.Replace(`
SELECT all_types.integer IS NULL AS "result.is_null",
all_types.date_ptr IS NOT NULL AS "result.is_not_null",
(all_types.small_int_ptr IN (?, ?)) AS "result.in",
(all_types.small_int_ptr IN (
SELECT all_types.integer AS "all_types.integer"
FROM all_types
)) AS "result.in_select",
(length(121232459)) AS "result.raw",
(? + COALESCE(all_types.small_int_ptr, 0) + ?) AS "result.raw_arg",
(? + all_types.integer + ? + ? + ? + ?) AS "result.raw_arg2",
(all_types.small_int_ptr NOT IN (?, ?, NULL)) AS "result.not_in",
(all_types.small_int_ptr NOT IN (
SELECT all_types.integer AS "all_types.integer"
FROM all_types
)) AS "result.not_in_select"
FROM all_types
LIMIT ?;
`, "'", "`", -1), int64(11), int64(22), 78, 56, 11, 22, 11, 33, 44, int64(11), int64(22), int64(2))
var dest []struct {
common.ExpressionTestResult `alias:"result.*"`
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, *dest[0].IsNull, false)
require.Equal(t, *dest[0].IsNotNull, true)
require.Equal(t, *dest[0].In, false)
require.Equal(t, *dest[0].InSelect, false)
require.Equal(t, *dest[0].Raw, "9")
require.Equal(t, *dest[0].RawArg, int32(148))
require.Equal(t, *dest[0].RawArg2, int32(-1479))
require.Nil(t, dest[0].NotIn)
require.Equal(t, *dest[0].NotInSelect, true)
}
func TestBoolOperators(t *testing.T) {
query := AllTypes.SELECT(
AllTypes.Boolean.EQ(AllTypes.BooleanPtr).AS("EQ1"),
AllTypes.Boolean.EQ(Bool(true)).AS("EQ2"),
AllTypes.Boolean.NOT_EQ(AllTypes.BooleanPtr).AS("NEq1"),
AllTypes.Boolean.NOT_EQ(Bool(false)).AS("NEq2"),
AllTypes.Boolean.IS_DISTINCT_FROM(AllTypes.BooleanPtr).AS("distinct1"),
AllTypes.Boolean.IS_DISTINCT_FROM(Bool(true)).AS("distinct2"),
AllTypes.Boolean.IS_NOT_DISTINCT_FROM(AllTypes.BooleanPtr).AS("not_distinct_1"),
AllTypes.Boolean.IS_NOT_DISTINCT_FROM(Bool(true)).AS("NOTDISTINCT2"),
AllTypes.Boolean.IS_TRUE().AS("ISTRUE"),
AllTypes.Boolean.IS_NOT_TRUE().AS("isnottrue"),
AllTypes.Boolean.IS_FALSE().AS("is_False"),
AllTypes.Boolean.IS_NOT_FALSE().AS("is not false"),
AllTypes.Boolean.IS_NULL().AS("is unknown"),
AllTypes.Boolean.IS_NOT_NULL().AS("is_not_unknown"),
AllTypes.Boolean.AND(AllTypes.Boolean).EQ(AllTypes.Boolean.AND(AllTypes.Boolean)).AS("complex1"),
AllTypes.Boolean.OR(AllTypes.Boolean).EQ(AllTypes.Boolean.AND(AllTypes.Boolean)).AS("complex2"),
)
testutils.AssertStatementSql(t, query, `
SELECT (all_types.boolean = all_types.boolean_ptr) AS "EQ1",
(all_types.boolean = ?) AS "EQ2",
(all_types.boolean != all_types.boolean_ptr) AS "NEq1",
(all_types.boolean != ?) AS "NEq2",
(all_types.boolean IS NOT all_types.boolean_ptr) AS "distinct1",
(all_types.boolean IS NOT ?) AS "distinct2",
(all_types.boolean IS all_types.boolean_ptr) AS "not_distinct_1",
(all_types.boolean IS ?) AS "NOTDISTINCT2",
all_types.boolean IS TRUE AS "ISTRUE",
all_types.boolean IS NOT TRUE AS "isnottrue",
all_types.boolean IS FALSE AS "is_False",
all_types.boolean IS NOT FALSE AS "is not false",
all_types.boolean IS NULL AS "is unknown",
all_types.boolean IS NOT NULL AS "is_not_unknown",
((all_types.boolean AND all_types.boolean) = (all_types.boolean AND all_types.boolean)) AS "complex1",
((all_types.boolean OR all_types.boolean) = (all_types.boolean AND all_types.boolean)) AS "complex2"
FROM all_types;
`, true, false, true, true)
var dest []struct {
Eq1 *bool
Eq2 *bool
NEq1 *bool
NEq2 *bool
Distinct1 *bool
Distinct2 *bool
NotDistinct1 *bool
NotDistinct2 *bool
IsTrue *bool
IsNotTrue *bool
IsFalse *bool
IsNotFalse *bool
IsUnknown *bool
IsNotUnknown *bool
Complex1 *bool
Complex2 *bool
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
testutils.AssertJSONFile(t, dest, "./testdata/results/common/bool_operators.json")
}
func TestFloatOperators(t *testing.T) {
query := AllTypes.SELECT(
AllTypes.Numeric.EQ(AllTypes.Numeric).AS("eq1"),
AllTypes.Decimal.EQ(Float(12.22)).AS("eq2"),
AllTypes.Real.EQ(Float(12.12)).AS("eq3"),
AllTypes.Numeric.IS_DISTINCT_FROM(AllTypes.Numeric).AS("distinct1"),
AllTypes.Decimal.IS_DISTINCT_FROM(Float(12)).AS("distinct2"),
AllTypes.Real.IS_DISTINCT_FROM(Float(12.12)).AS("distinct3"),
AllTypes.Numeric.IS_NOT_DISTINCT_FROM(AllTypes.Numeric).AS("not_distinct1"),
AllTypes.Decimal.IS_NOT_DISTINCT_FROM(Float(12)).AS("not_distinct2"),
AllTypes.Real.IS_NOT_DISTINCT_FROM(Float(12.12)).AS("not_distinct3"),
AllTypes.Numeric.LT(Float(124)).AS("lt1"),
AllTypes.Numeric.LT(Float(34.56)).AS("lt2"),
AllTypes.Numeric.GT(Float(124)).AS("gt1"),
AllTypes.Numeric.GT(Float(34.56)).AS("gt2"),
AllTypes.Decimal.ADD(AllTypes.Decimal).AS("add1"),
AllTypes.Decimal.ADD(Float(11.22)).AS("add2"),
AllTypes.Decimal.SUB(AllTypes.DecimalPtr).AS("sub1"),
AllTypes.Decimal.SUB(Float(11.22)).AS("sub2"),
AllTypes.Decimal.MUL(AllTypes.DecimalPtr).AS("mul1"),
AllTypes.Decimal.MUL(Float(11.22)).AS("mul2"),
AllTypes.Decimal.DIV(AllTypes.DecimalPtr).AS("div1"),
AllTypes.Decimal.DIV(Float(11.22)).AS("div2"),
AllTypes.Decimal.MOD(AllTypes.DecimalPtr).AS("mod1"),
AllTypes.Decimal.MOD(Float(11.22)).AS("mod2"),
// sqlite driver has to enable SQLITE_ENABLE_MATH_FUNCTIONS before commented math functions can be used
//AllTypes.Decimal.POW(AllTypes.DecimalPtr).AS("pow1"),
//AllTypes.Decimal.POW(Float(2.1)).AS("pow2"),
ABSf(AllTypes.Decimal).AS("abs"),
//POWER(AllTypes.Decimal, Float(2.1)).AS("power"),
//SQRT(AllTypes.Decimal).AS("sqrt"),
//CBRT(AllTypes.Decimal).AS("cbrt"),
//CEIL(AllTypes.Real).AS("ceil"),
//FLOOR(AllTypes.Real).AS("floor"),
ROUND(AllTypes.Decimal).AS("round1"),
ROUND(AllTypes.Decimal, Int(2)).AS("round2"),
//TRUNC(AllTypes.Decimal, Int(1)).AS("trunc"),
SIGN(AllTypes.Real).AS("sign"),
).LIMIT(1)
testutils.AssertStatementSql(t, query, `
SELECT (all_types.numeric = all_types.numeric) AS "eq1",
(all_types.decimal = ?) AS "eq2",
(all_types.real = ?) AS "eq3",
(all_types.numeric IS NOT all_types.numeric) AS "distinct1",
(all_types.decimal IS NOT ?) AS "distinct2",
(all_types.real IS NOT ?) AS "distinct3",
(all_types.numeric IS all_types.numeric) AS "not_distinct1",
(all_types.decimal IS ?) AS "not_distinct2",
(all_types.real IS ?) AS "not_distinct3",
(all_types.numeric < ?) AS "lt1",
(all_types.numeric < ?) AS "lt2",
(all_types.numeric > ?) AS "gt1",
(all_types.numeric > ?) AS "gt2",
(all_types.decimal + all_types.decimal) AS "add1",
(all_types.decimal + ?) AS "add2",
(all_types.decimal - all_types.decimal_ptr) AS "sub1",
(all_types.decimal - ?) AS "sub2",
(all_types.decimal * all_types.decimal_ptr) AS "mul1",
(all_types.decimal * ?) AS "mul2",
(all_types.decimal / all_types.decimal_ptr) AS "div1",
(all_types.decimal / ?) AS "div2",
(all_types.decimal % all_types.decimal_ptr) AS "mod1",
(all_types.decimal % ?) AS "mod2",
ABS(all_types.decimal) AS "abs",
ROUND(all_types.decimal) AS "round1",
ROUND(all_types.decimal, ?) AS "round2",
SIGN(all_types.real) AS "sign"
FROM all_types
LIMIT ?;
`)
var dest struct {
common.FloatExpressionTestResult `alias:"."`
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, *dest.Eq1, true)
require.Equal(t, *dest.Distinct1, false)
require.Equal(t, *dest.Lt1, true)
require.Equal(t, *dest.Add1, 2.22)
require.Equal(t, *dest.Mod2, float64(1))
require.Equal(t, *dest.Round1, float64(1))
require.Equal(t, *dest.Round2, float64(1.11))
require.Equal(t, *dest.Sign, float64(1))
//testutils.AssertJSONFile(t, dest, "./testdata/results/common/float_operators.json")
}
func TestIntegerOperators(t *testing.T) {
query := AllTypes.SELECT(
AllTypes.BigInt,
AllTypes.BigIntPtr,
AllTypes.SmallInt,
AllTypes.SmallIntPtr,
AllTypes.BigInt.EQ(AllTypes.BigInt).AS("eq1"),
AllTypes.BigInt.EQ(Int(12)).AS("eq2"),
AllTypes.BigInt.NOT_EQ(AllTypes.BigIntPtr).AS("neq1"),
AllTypes.BigInt.NOT_EQ(Int(12)).AS("neq2"),
AllTypes.BigInt.IS_DISTINCT_FROM(AllTypes.BigInt).AS("distinct1"),
AllTypes.BigInt.IS_DISTINCT_FROM(Int(12)).AS("distinct2"),
AllTypes.BigInt.IS_NOT_DISTINCT_FROM(AllTypes.BigInt).AS("not distinct1"),
AllTypes.BigInt.IS_NOT_DISTINCT_FROM(Int(12)).AS("not distinct2"),
AllTypes.BigInt.LT(AllTypes.BigIntPtr).AS("lt1"),
AllTypes.BigInt.LT(Int(65)).AS("lt2"),
AllTypes.BigInt.LT_EQ(AllTypes.BigIntPtr).AS("lte1"),
AllTypes.BigInt.LT_EQ(Int(65)).AS("lte2"),
AllTypes.BigInt.GT(AllTypes.BigIntPtr).AS("gt1"),
AllTypes.BigInt.GT(Int(65)).AS("gt2"),
AllTypes.BigInt.GT_EQ(AllTypes.BigIntPtr).AS("gte1"),
AllTypes.BigInt.GT_EQ(Int(65)).AS("gte2"),
AllTypes.BigInt.ADD(AllTypes.BigInt).AS("add1"),
AllTypes.BigInt.ADD(Int(11)).AS("add2"),
AllTypes.BigInt.SUB(AllTypes.BigInt).AS("sub1"),
AllTypes.BigInt.SUB(Int(11)).AS("sub2"),
AllTypes.BigInt.MUL(AllTypes.BigInt).AS("mul1"),
AllTypes.BigInt.MUL(Int(11)).AS("mul2"),
AllTypes.BigInt.DIV(AllTypes.BigInt).AS("div1"),
AllTypes.BigInt.DIV(Int(11)).AS("div2"),
AllTypes.BigInt.MOD(AllTypes.BigInt).AS("mod1"),
AllTypes.BigInt.MOD(Int(11)).AS("mod2"),
//AllTypes.SmallInt.POW(AllTypes.SmallInt.DIV(Int(3))).AS("pow1"),
//AllTypes.SmallInt.POW(Int(6)).AS("pow2"),
AllTypes.SmallInt.BIT_AND(AllTypes.SmallInt).AS("bit_and1"),
AllTypes.SmallInt.BIT_AND(AllTypes.SmallInt).AS("bit_and2"),
AllTypes.SmallInt.BIT_OR(AllTypes.SmallInt).AS("bit or 1"),
AllTypes.SmallInt.BIT_OR(Int(22)).AS("bit or 2"),
AllTypes.SmallInt.BIT_XOR(AllTypes.SmallInt).AS("bit xor 1"),
AllTypes.SmallInt.BIT_XOR(Int(11)).AS("bit xor 2"),
BIT_NOT(Int(-1).MUL(AllTypes.SmallInt)).AS("bit_not_1"),
BIT_NOT(Int(-1).MUL(Int(11))).AS("bit_not_2"),
AllTypes.SmallInt.BIT_SHIFT_LEFT(AllTypes.SmallInt.DIV(Int(2))).AS("bit shift left 1"),
AllTypes.SmallInt.BIT_SHIFT_LEFT(Int(4)).AS("bit shift left 2"),
AllTypes.SmallInt.BIT_SHIFT_RIGHT(AllTypes.SmallInt.DIV(Int(5))).AS("bit shift right 1"),
AllTypes.SmallInt.BIT_SHIFT_RIGHT(Int(1)).AS("bit shift right 2"),
ABSi(AllTypes.BigInt).AS("abs"),
//SQRT(ABSi(AllTypes.BigInt)).AS("sqrt"),
//CBRT(ABSi(AllTypes.BigInt)).AS("cbrt"),
).LIMIT(2)
var dest []struct {
common.AllTypesIntegerExpResult `alias:"."`
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, *dest[0].Eq1, true)
require.Equal(t, *dest[0].Distinct2, true)
require.Equal(t, *dest[0].Lt2, false)
require.Equal(t, *dest[0].Add1, int64(10000))
require.Equal(t, *dest[0].Mul1, int64(25000000))
require.Equal(t, *dest[0].Div2, int64(454))
require.Equal(t, *dest[0].BitAnd1, int64(14))
require.Equal(t, *dest[0].BitXor2, int64(5))
require.Equal(t, *dest[0].BitShiftLeft1, int64(1792))
require.Equal(t, *dest[0].BitShiftRight2, int64(7))
}
func TestStringOperators(t *testing.T) {
query := SELECT(
AllTypes.Text.EQ(AllTypes.Char),
AllTypes.Text.EQ(String("Text")),
AllTypes.Text.NOT_EQ(AllTypes.VarCharPtr),
AllTypes.Text.NOT_EQ(String("Text")),
AllTypes.Text.GT(AllTypes.Text),
AllTypes.Text.GT(String("Text")),
AllTypes.Text.GT_EQ(AllTypes.TextPtr),
AllTypes.Text.GT_EQ(String("Text")),
AllTypes.Text.LT(AllTypes.Char),
AllTypes.Text.LT(String("Text")),
AllTypes.Text.LT_EQ(AllTypes.VarCharPtr),
AllTypes.Text.LT_EQ(String("Text")),
AllTypes.Text.CONCAT(String("text2")),
AllTypes.Text.CONCAT(Int(11)),
AllTypes.Text.LIKE(String("abc")),
AllTypes.Text.NOT_LIKE(String("_b_")),
//AllTypes.Text.REGEXP_LIKE(String("aba")),
//AllTypes.Text.REGEXP_LIKE(String("aba"), false),
//String("ABA").REGEXP_LIKE(String("aba"), true),
//AllTypes.Text.NOT_REGEXP_LIKE(String("aba")),
//AllTypes.Text.NOT_REGEXP_LIKE(String("aba"), false),
//String("ABA").NOT_REGEXP_LIKE(String("aba"), true),
//BIT_LENGTH(AllTypes.Text),
//CHAR_LENGTH(AllTypes.Char),
//OCTET_LENGTH(AllTypes.Text),
LOWER(AllTypes.VarCharPtr),
UPPER(AllTypes.Char),
LTRIM(AllTypes.VarCharPtr),
RTRIM(AllTypes.VarCharPtr),
//CONCAT(String("string1"), Int(1), Float(11.12)),
//CONCAT_WS(String("string1"), Int(1), Float(11.12)),
//FORMAT(String("Hello %s, %1$s"), String("World")),
//LEFTSTR(String("abcde"), Int(2)),
//RIGHTSTR(String("abcde"), Int(2)),
LENGTH(String("jose")),
//LPAD(String("Hi"), Int(5), String("xy")),
//RPAD(String("Hi"), Int(5), String("xy")),
//MD5(AllTypes.VarCharPtr),
//REPEAT(AllTypes.Text, Int(33)),
REPLACE(AllTypes.Char, String("BA"), String("AB")),
//REVERSE(AllTypes.VarCharPtr),
SUBSTR(AllTypes.CharPtr, Int(3)),
SUBSTR(AllTypes.CharPtr, Int(3), Int(2)),
).FROM(AllTypes)
dest := []struct{}{}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
}
func TestReservedWord(t *testing.T) {
stmt := SELECT(ReservedWords.AllColumns).
FROM(ReservedWords)
testutils.AssertDebugStatementSql(t, stmt, strings.Replace(`
SELECT ''ReservedWords''.''column'' AS "ReservedWords.column",
''ReservedWords''.use AS "ReservedWords.use",
''ReservedWords''.ceil AS "ReservedWords.ceil",
''ReservedWords''.''commit'' AS "ReservedWords.commit",
''ReservedWords''.''create'' AS "ReservedWords.create",
''ReservedWords''.''default'' AS "ReservedWords.default",
''ReservedWords''.''desc'' AS "ReservedWords.desc",
''ReservedWords''.empty AS "ReservedWords.empty",
''ReservedWords''.float AS "ReservedWords.float",
''ReservedWords''.''join'' AS "ReservedWords.join",
''ReservedWords''.''like'' AS "ReservedWords.like",
''ReservedWords''.max AS "ReservedWords.max",
''ReservedWords''.rank AS "ReservedWords.rank"
FROM ''ReservedWords'';
`, "''", "`", -1))
var dest model.ReservedWords
err := stmt.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, dest, model.ReservedWords{
Column: "Column",
Use: "CHECK",
Ceil: "CEIL",
Commit: "COMMIT",
Create: "CREATE",
Default: "DEFAULT",
Desc: "DESC",
Empty: "EMPTY",
Float: "FLOAT",
Join: "JOIN",
Like: "LIKE",
Max: "MAX",
Rank: "RANK",
})
}
func TestExactDecimals(t *testing.T) {
type exactDecimals struct {
model.ExactDecimals
Decimal decimal.Decimal
DecimalPtr decimal.Decimal
}
t.Run("should query decimal", func(t *testing.T) {
query := SELECT(
ExactDecimals.AllColumns,
).FROM(
ExactDecimals,
).WHERE(ExactDecimals.Decimal.EQ(String("1.11111111111111111111")))
var result exactDecimals
err := query.Query(sampleDB, &result)
require.NoError(t, err)
require.Equal(t, "1.11111111111111111111", result.Decimal.String())
require.Equal(t, "0", result.DecimalPtr.String()) // NULL
require.Equal(t, "1.11111111111111111111", result.ExactDecimals.Decimal) // precision loss
require.Equal(t, (*string)(nil), result.ExactDecimals.DecimalPtr)
require.Equal(t, "2.22222222222222222222", result.ExactDecimals.Numeric)
require.Equal(t, (*string)(nil), result.ExactDecimals.NumericPtr) // NULL
})
t.Run("should insert decimal", func(t *testing.T) {
insertQuery := ExactDecimals.INSERT(
ExactDecimals.AllColumns,
).MODEL(
exactDecimals{
ExactDecimals: model.ExactDecimals{
// overwritten by wrapped(exactDecimals) scope
Decimal: "0.1",
DecimalPtr: nil,
// not overwritten
Numeric: "6.7",
NumericPtr: testutils.StringPtr("7.7"),
},
Decimal: decimal.RequireFromString("91.23"),
DecimalPtr: decimal.RequireFromString("45.67"),
},
).RETURNING(ExactDecimals.AllColumns)
testutils.AssertDebugStatementSql(t, insertQuery, strings.Replace(`
INSERT INTO exact_decimals (decimal, decimal_ptr, numeric, numeric_ptr)
VALUES ('91.23', '45.67', '6.7', '7.7')
RETURNING exact_decimals.decimal AS "exact_decimals.decimal",
exact_decimals.decimal_ptr AS "exact_decimals.decimal_ptr",
exact_decimals.numeric AS "exact_decimals.numeric",
exact_decimals.numeric_ptr AS "exact_decimals.numeric_ptr";
`, "''", "`", -1))
tx := beginSampleDBTx(t)
defer tx.Rollback()
var result exactDecimals
err := insertQuery.Query(tx, &result)
require.NoError(t, err)
require.Equal(t, "91.23", result.Decimal.String())
require.Equal(t, "45.67", result.DecimalPtr.String())
require.Equal(t, "6.7", result.ExactDecimals.Numeric)
require.Equal(t, "7.7", *result.ExactDecimals.NumericPtr)
require.Equal(t, "91.23", result.ExactDecimals.Decimal)
require.Equal(t, "45.67", *result.ExactDecimals.DecimalPtr)
})
}
var timeT = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
func TestDateExpressions(t *testing.T) {
query := AllTypes.SELECT(
//Date(2009, 11, 17, 2, MONTH, 1, DAY),
//DateT(timeT, START_OF_THE_MONTH),
AllTypes.Date.AS("date"),
DATE("2009-11-17").AS("date1"),
DATE("2013-10-07 08:23:19.120", DAYS(1)).AS("date2"),
DATE(AllTypes.Date, START_OF_YEAR, DAYS(2)).AS("date3"),
DATE(timeT, START_OF_MONTH).AS("date3"),
DATE("now", WEEKDAY(1)).AS("date4"),
DATE(timeT.Unix(), UNIXEPOCH).AS("date5"),
DATE(time.Now(), UTC).AS("date6"),
DATE(time.Now().UTC(), LOCALTIME).AS("date7"),
AllTypes.Date.EQ(AllTypes.Date),
AllTypes.Date.EQ(Date(2019, 6, 6)),
AllTypes.DatePtr.NOT_EQ(AllTypes.Date),
AllTypes.DatePtr.NOT_EQ(Date(2019, 1, 6)),
AllTypes.Date.IS_DISTINCT_FROM(AllTypes.Date).AS("distinct1"),
AllTypes.Date.IS_DISTINCT_FROM(Date(2008, 7, 4)).AS("distinct2"),
AllTypes.Date.IS_NOT_DISTINCT_FROM(AllTypes.Date),
AllTypes.Date.IS_NOT_DISTINCT_FROM(Date(2019, 3, 6)),
AllTypes.Date.LT(AllTypes.Date),
AllTypes.Date.LT(Date(2019, 4, 6)),
AllTypes.Date.LT_EQ(AllTypes.Date),
AllTypes.Date.LT_EQ(Date(2019, 5, 5)),
AllTypes.Date.GT(AllTypes.Date),
AllTypes.Date.GT(Date(2019, 1, 4)),
AllTypes.Date.GT_EQ(AllTypes.Date),
AllTypes.Date.GT_EQ(Date(2019, 2, 3)),
//AllTypes.Date.ADD(INTERVAL2(2, HOUR)),
//AllTypes.Date.ADD(INTERVAL2(1, DAY, 7, MONTH)),
//AllTypes.Date.ADD(INTERVALd(25 * time.Hour + 100 * time.Millisecond)),
//AllTypes.Date.ADD(INTERVALd(-25 * time.Hour - 100 * time.Millisecond)),
//
//AllTypes.Date.SUB(INTERVAL(20, MINUTE)),
//AllTypes.Date.SUB(INTERVALe(AllTypes.SmallInt, MINUTE)),
//AllTypes.Date.SUB(INTERVALd(3*time.Minute)),
CURRENT_DATE().AS("current_date"),
)
var dest struct {
Date string
Date1 time.Time
Date2 string
Date3 time.Time
Date4 string
Date5 time.Time
Date6 string
Date7 time.Time
Distinct1 bool
Distinct2 bool
CurrentDate time.Time
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, dest.Date, "2008-07-04T00:00:00Z")
require.Equal(t, dest.Date1.Unix(), int64(1258416000))
}
func TestTimeExpressions(t *testing.T) {
query := AllTypes.SELECT(
TIME(AllTypes.Time).AS("time1"),
TIME(timeT).AS("time2"),
TIME("04:23:19.120-04:00", HOURS(1), MINUTES(2), SECONDS(1.234)).AS("time3"),
TIME(timeT.Unix(), UNIXEPOCH).AS("time4"),
TIME(time.Now(), UTC).AS("time5"),
TIME(time.Now().UTC(), LOCALTIME).AS("time6"),
Time(timeT.Clock()),
AllTypes.Time.EQ(AllTypes.Time),
AllTypes.Time.EQ(Time(23, 6, 6)),
AllTypes.Time.EQ(Time(22, 6, 6, 11*time.Millisecond)),
AllTypes.Time.EQ(Time(21, 6, 6, 11111*time.Microsecond)),
AllTypes.TimePtr.NOT_EQ(AllTypes.Time),
AllTypes.TimePtr.NOT_EQ(Time(20, 16, 6)),
AllTypes.Time.IS_DISTINCT_FROM(AllTypes.Time),
AllTypes.Time.IS_DISTINCT_FROM(Time(19, 26, 6)),
AllTypes.Time.IS_NOT_DISTINCT_FROM(AllTypes.Time),
AllTypes.Time.IS_NOT_DISTINCT_FROM(Time(18, 36, 6)),
AllTypes.Time.LT(AllTypes.Time),
AllTypes.Time.LT(Time(17, 46, 6)),
AllTypes.Time.LT_EQ(AllTypes.Time),
AllTypes.Time.LT_EQ(Time(16, 56, 56)),
AllTypes.Time.GT(AllTypes.Time),
AllTypes.Time.GT(Time(15, 16, 46)),
AllTypes.Time.GT_EQ(AllTypes.Time),
AllTypes.Time.GT_EQ(Time(14, 26, 36)),
//AllTypes.Time.ADD(INTERVAL(10, MINUTE)),
//AllTypes.Time.ADD(INTERVALe(AllTypes.Integer, MINUTE)),
//AllTypes.Time.ADD(INTERVALd(3*time.Hour)),
//
//AllTypes.Time.SUB(INTERVAL(20, MINUTE)),
//AllTypes.Time.SUB(INTERVALe(AllTypes.SmallInt, MINUTE)),
//AllTypes.Time.SUB(INTERVALd(3*time.Minute)),
//
//AllTypes.Time.ADD(INTERVAL(20, MINUTE)).SUB(INTERVAL(11, HOUR)),
CURRENT_TIME(),
)
var dest struct {
Time1 string
Time2 time.Time
Time3 string
Time4 time.Time
Time5 string
Time6 time.Time
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
require.Equal(t, dest.Time1, "10:11:12")
require.Equal(t, dest.Time2.UTC().String(), "0000-01-01 20:34:58 +0000 UTC")
require.Equal(t, dest.Time3, "09:25:20")
}
func TestDateTimeExpressions(t *testing.T) {
var dateTime = DateTime(2019, 6, 6, 10, 2, 46)
query := SELECT(
DATETIME("now").AS("now"),
DATETIME("2013-10-07T08:23:19.120Z", YEARS(2), MONTHS(1), DAYS(1)).AS("datetime1"),
DATETIME(AllTypes.DateTime, MONTHS(1), DAYS(1)).AS("datetime2"),
DATETIME(timeT.Unix(), UNIXEPOCH).AS("datetime3"),
DATETIME(time.Now(), UTC).AS("datetime4"),
DATETIME(timeT.UTC(), LOCALTIME).AS("datetime5"),
JULIANDAY(timeT, DAYS(1)).AS("JulianDay"),
STRFTIME(String("%H:%M"), timeT, SECONDS(1.22)).AS("strftime"),
AllTypes.DateTime.EQ(AllTypes.DateTime),
AllTypes.DateTime.EQ(dateTime),
AllTypes.DateTimePtr.NOT_EQ(AllTypes.DateTime),
AllTypes.DateTimePtr.NOT_EQ(DateTime(2019, 6, 6, 10, 2, 46, 100*time.Millisecond)),
AllTypes.DateTime.IS_DISTINCT_FROM(AllTypes.DateTime),
AllTypes.DateTime.IS_DISTINCT_FROM(dateTime),
AllTypes.DateTime.IS_NOT_DISTINCT_FROM(AllTypes.DateTime),
AllTypes.DateTime.IS_NOT_DISTINCT_FROM(dateTime),
AllTypes.DateTime.LT(AllTypes.DateTime),
AllTypes.DateTime.LT(dateTime),
AllTypes.DateTime.LT_EQ(AllTypes.DateTime),
AllTypes.DateTime.LT_EQ(dateTime),
AllTypes.DateTime.GT(AllTypes.DateTime),
AllTypes.DateTime.GT(dateTime),
AllTypes.DateTime.GT_EQ(AllTypes.DateTime),
AllTypes.DateTime.GT_EQ(dateTime),
//AllTypes.DateTime.ADD(INTERVAL("05:10:20.000100", HOUR_MICROSECOND)),
//AllTypes.DateTime.ADD(INTERVALe(AllTypes.BigInt, HOUR)),
//AllTypes.DateTime.ADD(INTERVALd(2*time.Hour)),
//
//AllTypes.DateTime.SUB(INTERVAL("05:10:20.000100", HOUR_MICROSECOND)),
//AllTypes.DateTime.SUB(INTERVALe(AllTypes.IntegerPtr, HOUR)),
//AllTypes.DateTime.SUB(INTERVALd(3*time.Hour)),
CURRENT_TIMESTAMP(),
).FROM(AllTypes)
var dest struct {
Now time.Time
DateTime1 time.Time
DateTime2 time.Time
DateTime3 time.Time
DateTime4 time.Time
DateTime5 time.Time
JulianDay float64
StrfTime string
}
err := query.Query(sampleDB, &dest)
require.NoError(t, err)
require.True(t, dest.Now.After(time.Now().Add(-1*time.Minute)))
require.Equal(t, dest.DateTime1.String(), "2015-11-08 08:23:19 +0000 UTC")
require.Equal(t, dest.DateTime2.String(), "2012-01-19 13:17:17 +0000 UTC")
require.Equal(t, dest.DateTime3.String(), "2009-11-17 20:34:58 +0000 UTC")
require.True(t, dest.DateTime4.After(time.Now().Add(-1*time.Minute)))
require.Equal(t, dest.DateTime5.String(), "2009-11-17 21:34:58 +0000 UTC")
require.Equal(t, dest.JulianDay, 2.4551543576232754e+06)
require.Equal(t, dest.StrfTime, "20:34")
}

41
tests/sqlite/cast_test.go Normal file
View file

@ -0,0 +1,41 @@
package sqlite
import (
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
"github.com/stretchr/testify/require"
"testing"
)
func TestCast(t *testing.T) {
query := SELECT(
CAST(String("test")).AS("CHARACTER").AS("result.AS1"),
CAST(Float(11.33)).AS_TEXT().AS("result.text"),
CAST(String("33.44")).AS_REAL().AS("result.real"),
CAST(String("33")).AS_INTEGER().AS("result.integer"),
CAST(String("Blob blob")).AS_BLOB().AS("result.blob"),
)
type Result struct {
As1 string
Text string
Real float64
Integer int64
Blob []byte
}
var dest Result
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertDeepEqual(t, dest, Result{
As1: "test",
Text: "11.33",
Real: 33.44,
Integer: 33,
Blob: []byte("Blob blob"),
})
requireLogged(t, query)
}

View file

@ -0,0 +1,83 @@
package sqlite
import (
"context"
"testing"
"time"
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/table"
"github.com/stretchr/testify/require"
)
func TestDelete_WHERE_RETURNING(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
var expectedSQL = `
DELETE FROM link
WHERE link.name IN ('Bing', 'Yahoo')
RETURNING link.id AS "link.id",
link.url AS "link.url",
link.name AS "link.name",
link.description AS "link.description";
`
deleteStmt := Link.DELETE().
WHERE(Link.Name.IN(String("Bing"), String("Yahoo"))).
RETURNING(Link.AllColumns)
testutils.AssertDebugStatementSql(t, deleteStmt, expectedSQL, "Bing", "Yahoo")
var dest []model.Link
err := deleteStmt.Query(tx, &dest)
require.NoError(t, err)
require.Len(t, dest, 2)
requireLogged(t, deleteStmt)
}
func TestDeleteWithWhereOrderByLimit(t *testing.T) {
t.SkipNow() // Until https://github.com/mattn/go-sqlite3/pull/802 is fixed
tx := beginSampleDBTx(t)
defer tx.Rollback()
sampleDB.Stats()
var expectedSQL = `
DELETE FROM link
WHERE link.name IN ('Bing', 'Yahoo')
ORDER BY link.name
LIMIT 1;
`
deleteStmt := Link.DELETE().
WHERE(Link.Name.IN(String("Bing"), String("Yahoo"))).
ORDER_BY(Link.Name).
LIMIT(1)
testutils.AssertDebugStatementSql(t, deleteStmt, expectedSQL, "Bing", "Yahoo", int64(1))
testutils.AssertExec(t, deleteStmt, tx, 1)
requireLogged(t, deleteStmt)
}
func TestDeleteContextDeadlineExceeded(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
deleteStmt := Link.
DELETE().
WHERE(Link.Name.IN(String("Bing"), String("Yahoo")))
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond)
defer cancel()
time.Sleep(10 * time.Millisecond)
dest := []model.Link{}
err := deleteStmt.QueryContext(ctx, tx, &dest)
require.Error(t, err, "context deadline exceeded")
_, err = deleteStmt.ExecContext(ctx, tx)
require.Error(t, err, "context deadline exceeded")
requireLogged(t, deleteStmt)
}

View file

@ -0,0 +1,298 @@
package sqlite
import (
"github.com/go-jet/jet/v2/generator/sqlite"
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/sakila/model"
"github.com/go-jet/jet/v2/tests/internal/utils/repo"
"github.com/stretchr/testify/require"
"io/ioutil"
"os"
"os/exec"
"reflect"
"testing"
)
func TestGeneratedModel(t *testing.T) {
actor := model.Actor{}
require.Equal(t, reflect.TypeOf(actor.ActorID).String(), "int32")
actorIDField, ok := reflect.TypeOf(actor).FieldByName("ActorID")
require.True(t, ok)
require.Equal(t, actorIDField.Tag.Get("sql"), "primary_key")
require.Equal(t, reflect.TypeOf(actor.FirstName).String(), "string")
require.Equal(t, reflect.TypeOf(actor.LastName).String(), "string")
require.Equal(t, reflect.TypeOf(actor.LastUpdate).String(), "time.Time")
filmActor := model.FilmActor{}
require.Equal(t, reflect.TypeOf(filmActor.FilmID).String(), "int32")
filmIDField, ok := reflect.TypeOf(filmActor).FieldByName("FilmID")
require.True(t, ok)
require.Equal(t, filmIDField.Tag.Get("sql"), "primary_key")
require.Equal(t, reflect.TypeOf(filmActor.ActorID).String(), "int32")
actorIDField, ok = reflect.TypeOf(filmActor).FieldByName("ActorID")
require.True(t, ok)
require.Equal(t, filmIDField.Tag.Get("sql"), "primary_key")
staff := model.Staff{}
require.Equal(t, reflect.TypeOf(staff.Email).String(), "*string")
require.Equal(t, reflect.TypeOf(staff.Picture).String(), "*[]uint8")
}
var testDatabaseFilePath = repo.GetTestDataFilePath("/init/sqlite/sakila.db")
var genDestDir = repo.GetTestsFilePath("/sqlite/.gen")
func TestGenerator(t *testing.T) {
for i := 0; i < 3; i++ {
err := sqlite.GenerateDSN(testDatabaseFilePath, genDestDir)
require.NoError(t, err)
assertGeneratedFiles(t)
}
err := os.RemoveAll(genDestDir)
require.NoError(t, err)
}
func TestCmdGenerator(t *testing.T) {
cmd := exec.Command("jet", "-source=SQLite", "-dsn=file://"+testDatabaseFilePath, "-path="+genDestDir)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
err := cmd.Run()
require.NoError(t, err)
assertGeneratedFiles(t)
err = os.RemoveAll(genDestDir)
require.NoError(t, err)
}
func assertGeneratedFiles(t *testing.T) {
// Table SQL Builder files
tableSQLBuilderFiles, err := ioutil.ReadDir(genDestDir + "/table")
require.NoError(t, err)
testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "actor.go", "address.go", "category.go", "city.go", "country.go",
"customer.go", "film.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go",
"payment.go", "rental.go", "staff.go", "store.go")
testutils.AssertFileContent(t, genDestDir+"/table/actor.go", actorSQLBuilderFile)
// View SQL Builder files
viewSQLBuilderFiles, err := ioutil.ReadDir(genDestDir + "/view")
require.NoError(t, err)
testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "film_list.go", "sales_by_film_category.go",
"customer_list.go", "sales_by_store.go", "staff_list.go")
testutils.AssertFileContent(t, genDestDir+"/view/film_list.go", filmListSQLBuilderFile)
// Model files
modelFiles, err := ioutil.ReadDir(genDestDir + "/model")
require.NoError(t, err)
testutils.AssertFileNamesEqual(t, modelFiles, "actor.go", "address.go", "category.go", "city.go", "country.go",
"customer.go", "film.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go",
"payment.go", "rental.go", "staff.go", "store.go",
"film_list.go", "sales_by_film_category.go",
"customer_list.go", "sales_by_store.go", "staff_list.go")
testutils.AssertFileContent(t, genDestDir+"/model/address.go", addressModelFile)
}
const actorSQLBuilderFile = `
//
// Code generated by go-jet DO NOT EDIT.
//
// WARNING: Changes to this file may cause incorrect behavior
// and will be lost if the code is regenerated
//
package table
import (
"github.com/go-jet/jet/v2/sqlite"
)
var Actor = newActorTable("", "actor", "")
type actorTable struct {
sqlite.Table
//Columns
ActorID sqlite.ColumnInteger
FirstName sqlite.ColumnString
LastName sqlite.ColumnString
LastUpdate sqlite.ColumnTimestamp
AllColumns sqlite.ColumnList
MutableColumns sqlite.ColumnList
}
type ActorTable struct {
actorTable
EXCLUDED actorTable
}
// AS creates new ActorTable with assigned alias
func (a ActorTable) AS(alias string) *ActorTable {
return newActorTable(a.SchemaName(), a.TableName(), alias)
}
// Schema creates new ActorTable with assigned schema name
func (a ActorTable) FromSchema(schemaName string) *ActorTable {
return newActorTable(schemaName, a.TableName(), a.Alias())
}
func newActorTable(schemaName, tableName, alias string) *ActorTable {
return &ActorTable{
actorTable: newActorTableImpl(schemaName, tableName, alias),
EXCLUDED: newActorTableImpl("", "excluded", ""),
}
}
func newActorTableImpl(schemaName, tableName, alias string) actorTable {
var (
ActorIDColumn = sqlite.IntegerColumn("actor_id")
FirstNameColumn = sqlite.StringColumn("first_name")
LastNameColumn = sqlite.StringColumn("last_name")
LastUpdateColumn = sqlite.TimestampColumn("last_update")
allColumns = sqlite.ColumnList{ActorIDColumn, FirstNameColumn, LastNameColumn, LastUpdateColumn}
mutableColumns = sqlite.ColumnList{FirstNameColumn, LastNameColumn, LastUpdateColumn}
)
return actorTable{
Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...),
//Columns
ActorID: ActorIDColumn,
FirstName: FirstNameColumn,
LastName: LastNameColumn,
LastUpdate: LastUpdateColumn,
AllColumns: allColumns,
MutableColumns: mutableColumns,
}
}
`
const filmListSQLBuilderFile = `
//
// Code generated by go-jet DO NOT EDIT.
//
// WARNING: Changes to this file may cause incorrect behavior
// and will be lost if the code is regenerated
//
package view
import (
"github.com/go-jet/jet/v2/sqlite"
)
var FilmList = newFilmListTable("", "film_list", "")
type filmListTable struct {
sqlite.Table
//Columns
Fid sqlite.ColumnInteger
Title sqlite.ColumnString
Description sqlite.ColumnString
Category sqlite.ColumnString
Price sqlite.ColumnFloat
Length sqlite.ColumnInteger
Rating sqlite.ColumnString
Actors sqlite.ColumnString
AllColumns sqlite.ColumnList
MutableColumns sqlite.ColumnList
}
type FilmListTable struct {
filmListTable
EXCLUDED filmListTable
}
// AS creates new FilmListTable with assigned alias
func (a FilmListTable) AS(alias string) *FilmListTable {
return newFilmListTable(a.SchemaName(), a.TableName(), alias)
}
// Schema creates new FilmListTable with assigned schema name
func (a FilmListTable) FromSchema(schemaName string) *FilmListTable {
return newFilmListTable(schemaName, a.TableName(), a.Alias())
}
func newFilmListTable(schemaName, tableName, alias string) *FilmListTable {
return &FilmListTable{
filmListTable: newFilmListTableImpl(schemaName, tableName, alias),
EXCLUDED: newFilmListTableImpl("", "excluded", ""),
}
}
func newFilmListTableImpl(schemaName, tableName, alias string) filmListTable {
var (
FidColumn = sqlite.IntegerColumn("FID")
TitleColumn = sqlite.StringColumn("title")
DescriptionColumn = sqlite.StringColumn("description")
CategoryColumn = sqlite.StringColumn("category")
PriceColumn = sqlite.FloatColumn("price")
LengthColumn = sqlite.IntegerColumn("length")
RatingColumn = sqlite.StringColumn("rating")
ActorsColumn = sqlite.StringColumn("actors")
allColumns = sqlite.ColumnList{FidColumn, TitleColumn, DescriptionColumn, CategoryColumn, PriceColumn, LengthColumn, RatingColumn, ActorsColumn}
mutableColumns = sqlite.ColumnList{FidColumn, TitleColumn, DescriptionColumn, CategoryColumn, PriceColumn, LengthColumn, RatingColumn, ActorsColumn}
)
return filmListTable{
Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...),
//Columns
Fid: FidColumn,
Title: TitleColumn,
Description: DescriptionColumn,
Category: CategoryColumn,
Price: PriceColumn,
Length: LengthColumn,
Rating: RatingColumn,
Actors: ActorsColumn,
AllColumns: allColumns,
MutableColumns: mutableColumns,
}
}
`
const addressModelFile = `
//
// Code generated by go-jet DO NOT EDIT.
//
// WARNING: Changes to this file may cause incorrect behavior
// and will be lost if the code is regenerated
//
package model
import (
"time"
)
type Address struct {
AddressID int32 ` + "`sql:\"primary_key\"`" + `
Address string
Address2 *string
District string
CityID int32
PostalCode *string
Phone string
LastUpdate time.Time
}
`

393
tests/sqlite/insert_test.go Normal file
View file

@ -0,0 +1,393 @@
package sqlite
import (
"context"
"math/rand"
"testing"
"time"
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/table"
"github.com/stretchr/testify/require"
)
func TestInsertValues(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
insertQuery := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description).
VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil).
VALUES(101, "http://www.google.com", "Google", "Search engine").
VALUES(102, "http://www.yahoo.com", "Yahoo", nil)
testutils.AssertStatementSql(t, insertQuery, `
INSERT INTO link (id, url, name, description)
VALUES (?, ?, ?, ?),
(?, ?, ?, ?),
(?, ?, ?, ?);
`, 100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil,
101, "http://www.google.com", "Google", "Search engine",
102, "http://www.yahoo.com", "Yahoo", nil)
_, err := insertQuery.Exec(tx)
require.NoError(t, err)
requireLogged(t, insertQuery)
insertedLinks := []model.Link{}
err = SELECT(Link.AllColumns).
FROM(Link).
WHERE(Link.ID.GT_EQ(Int(100))).
ORDER_BY(Link.ID).
Query(tx, &insertedLinks)
require.NoError(t, err)
require.Equal(t, len(insertedLinks), 3)
testutils.AssertDeepEqual(t, insertedLinks[0], postgreTutorial)
testutils.AssertDeepEqual(t, insertedLinks[1], model.Link{
ID: 101,
URL: "http://www.google.com",
Name: "Google",
Description: testutils.StringPtr("Search engine"),
})
testutils.AssertDeepEqual(t, insertedLinks[2], model.Link{
ID: 102,
URL: "http://www.yahoo.com",
Name: "Yahoo",
})
}
var postgreTutorial = model.Link{
ID: 100,
URL: "http://www.postgresqltutorial.com",
Name: "PostgreSQL Tutorial",
}
func TestInsertEmptyColumnList(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
expectedSQL := `
INSERT INTO link
VALUES (100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', NULL);
`
stmt := Link.INSERT().
VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil)
testutils.AssertDebugStatementSql(t, stmt, expectedSQL,
100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil)
_, err := stmt.Exec(tx)
require.NoError(t, err)
requireLogged(t, stmt)
insertedLinks := []model.Link{}
err = SELECT(Link.AllColumns).
FROM(Link).
WHERE(Link.ID.GT_EQ(Int(100))).
ORDER_BY(Link.ID).
Query(tx, &insertedLinks)
require.NoError(t, err)
require.Equal(t, len(insertedLinks), 1)
testutils.AssertDeepEqual(t, insertedLinks[0], postgreTutorial)
}
func TestInsertModelObject(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
linkData := model.Link{
URL: "http://www.duckduckgo.com",
Name: "Duck Duck go",
}
query := Link.INSERT(Link.URL, Link.Name).
MODEL(linkData)
testutils.AssertDebugStatementSql(t, query, `
INSERT INTO link (url, name)
VALUES ('http://www.duckduckgo.com', 'Duck Duck go');
`, "http://www.duckduckgo.com", "Duck Duck go")
_, err := query.Exec(tx)
require.NoError(t, err)
}
func TestInsertModelObjectEmptyColumnList(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
var expectedSQL = `
INSERT INTO link
VALUES (1000, 'http://www.duckduckgo.com', 'Duck Duck go', NULL);
`
linkData := model.Link{
ID: 1000,
URL: "http://www.duckduckgo.com",
Name: "Duck Duck go",
}
query := Link.
INSERT().
MODEL(linkData)
testutils.AssertDebugStatementSql(t, query, expectedSQL, int32(1000), "http://www.duckduckgo.com", "Duck Duck go", nil)
_, err := query.Exec(tx)
require.NoError(t, err)
}
func TestInsertModelsObject(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
expectedSQL := `
INSERT INTO link (url, name)
VALUES ('http://www.postgresqltutorial.com', 'PostgreSQL Tutorial'),
('http://www.google.com', 'Google'),
('http://www.yahoo.com', 'Yahoo');
`
tutorial := model.Link{
URL: "http://www.postgresqltutorial.com",
Name: "PostgreSQL Tutorial",
}
google := model.Link{
URL: "http://www.google.com",
Name: "Google",
}
yahoo := model.Link{
URL: "http://www.yahoo.com",
Name: "Yahoo",
}
query := Link.
INSERT(Link.URL, Link.Name).
MODELS([]model.Link{
tutorial,
google,
yahoo,
})
testutils.AssertDebugStatementSql(t, query, expectedSQL,
"http://www.postgresqltutorial.com", "PostgreSQL Tutorial",
"http://www.google.com", "Google",
"http://www.yahoo.com", "Yahoo")
_, err := query.Exec(tx)
require.NoError(t, err)
}
func TestInsertUsingMutableColumns(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
var expectedSQL = `
INSERT INTO link (url, name, description)
VALUES ('http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', NULL),
('http://www.google.com', 'Google', NULL),
('http://www.google.com', 'Google', NULL),
('http://www.yahoo.com', 'Yahoo', NULL);
`
google := model.Link{
URL: "http://www.google.com",
Name: "Google",
}
yahoo := model.Link{
URL: "http://www.yahoo.com",
Name: "Yahoo",
}
stmt := Link.
INSERT(Link.MutableColumns).
VALUES("http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil).
MODEL(google).
MODELS([]model.Link{google, yahoo})
testutils.AssertDebugStatementSql(t, stmt, expectedSQL,
"http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil,
"http://www.google.com", "Google", nil,
"http://www.google.com", "Google", nil,
"http://www.yahoo.com", "Yahoo", nil)
_, err := stmt.Exec(tx)
require.NoError(t, err)
}
func TestInsertQuery(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
var expectedSQL = `
INSERT INTO link (url, name)
SELECT link.url AS "link.url",
link.name AS "link.name"
FROM link
WHERE link.id = 24;
`
query := Link.INSERT(Link.URL, Link.Name).
QUERY(
SELECT(Link.URL, Link.Name).
FROM(Link).
WHERE(Link.ID.EQ(Int(24))),
)
testutils.AssertDebugStatementSql(t, query, expectedSQL, int64(24))
_, err := query.Exec(tx)
require.NoError(t, err)
youtubeLinks := []model.Link{}
err = Link.
SELECT(Link.AllColumns).
WHERE(Link.Name.EQ(String("Bing"))).
Query(tx, &youtubeLinks)
require.NoError(t, err)
require.Equal(t, len(youtubeLinks), 2)
}
func TestInsert_DEFAULT_VALUES_RETURNING(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
stmt := Link.INSERT().
DEFAULT_VALUES().
RETURNING(Link.AllColumns)
testutils.AssertDebugStatementSql(t, stmt, `
INSERT INTO link
DEFAULT VALUES
RETURNING link.id AS "link.id",
link.url AS "link.url",
link.name AS "link.name",
link.description AS "link.description";
`)
var link model.Link
err := stmt.Query(tx, &link)
require.NoError(t, err)
require.EqualValues(t, link, model.Link{
ID: 25,
URL: "www.",
Name: "_",
Description: nil,
})
}
func TestInsertOnConflict(t *testing.T) {
t.Run("do nothing", func(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
link := model.Link{ID: rand.Int31()}
stmt := Link.INSERT(Link.AllColumns).
MODEL(link).
MODEL(link).
ON_CONFLICT(Link.ID).DO_NOTHING()
testutils.AssertStatementSql(t, stmt, `
INSERT INTO link (id, url, name, description)
VALUES (?, ?, ?, ?),
(?, ?, ?, ?)
ON CONFLICT (id) DO NOTHING;
`)
testutils.AssertExec(t, stmt, tx, 1)
requireLogged(t, stmt)
})
t.Run("do update", func(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
stmt := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description).
VALUES(21, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil).
VALUES(22, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil).
ON_CONFLICT(Link.ID).
DO_UPDATE(
SET(
Link.ID.SET(Link.EXCLUDED.ID),
Link.URL.SET(String("http://www.postgresqltutorial2.com")),
),
).RETURNING(Link.AllColumns)
testutils.AssertStatementSql(t, stmt, `
INSERT INTO link (id, url, name, description)
VALUES (?, ?, ?, ?),
(?, ?, ?, ?)
ON CONFLICT (id) DO UPDATE
SET id = excluded.id,
url = ?
RETURNING link.id AS "link.id",
link.url AS "link.url",
link.name AS "link.name",
link.description AS "link.description";
`)
testutils.AssertExec(t, stmt, tx)
requireLogged(t, stmt)
})
t.Run("do update complex", func(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
stmt := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description).
VALUES(21, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil).
ON_CONFLICT(Link.ID).
WHERE(Link.ID.MUL(Int(2)).GT(Int(10))).
DO_UPDATE(
SET(
Link.ID.SET(
IntExp(SELECT(MAXi(Link.ID).ADD(Int(1))).
FROM(Link)),
),
ColumnList{Link.Name, Link.Description}.SET(ROW(Link.EXCLUDED.Name, String(""))),
).WHERE(Link.Description.IS_NOT_NULL()),
)
testutils.AssertDebugStatementSql(t, stmt, `
INSERT INTO link (id, url, name, description)
VALUES (21, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', NULL)
ON CONFLICT (id) WHERE (id * 2) > 10 DO UPDATE
SET id = (
SELECT MAX(link.id) + 1
FROM link
),
(name, description) = (excluded.name, '')
WHERE link.description IS NOT NULL;
`)
testutils.AssertExec(t, stmt, tx)
requireLogged(t, stmt)
})
}
func TestInsertContextDeadlineExceeded(t *testing.T) {
stmt := Link.INSERT().
VALUES(1100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", nil)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond)
defer cancel()
time.Sleep(10 * time.Millisecond)
dest := []model.Link{}
err := stmt.QueryContext(ctx, sampleDB, &dest)
require.Error(t, err, "context deadline exceeded")
_, err = stmt.ExecContext(ctx, db)
require.Error(t, err, "context deadline exceeded")
}

90
tests/sqlite/main_test.go Normal file
View file

@ -0,0 +1,90 @@
package sqlite
import (
"context"
"database/sql"
"fmt"
"github.com/go-jet/jet/v2/internal/utils/throw"
"github.com/go-jet/jet/v2/sqlite"
"github.com/go-jet/jet/v2/tests/dbconfig"
"github.com/stretchr/testify/require"
"math/rand"
"os"
"os/exec"
"strings"
"testing"
"time"
"github.com/pkg/profile"
_ "github.com/mattn/go-sqlite3"
)
var db *sql.DB
var sampleDB *sql.DB
var testRoot string
func TestMain(m *testing.M) {
rand.Seed(time.Now().Unix())
defer profile.Start().Stop()
setTestRoot()
var err error
db, err = sql.Open("sqlite3", "file:"+dbconfig.SakilaDBPath)
throw.OnError(err)
_, err = db.Exec(fmt.Sprintf("ATTACH DATABASE '%s' as 'chinook';", dbconfig.ChinookDBPath))
throw.OnError(err)
sampleDB, err = sql.Open("sqlite3", dbconfig.TestSampleDBPath)
throw.OnError(err)
defer db.Close()
ret := m.Run()
if ret != 0 {
os.Exit(ret)
}
}
func setTestRoot() {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
byteArr, err := cmd.Output()
if err != nil {
panic(err)
}
testRoot = strings.TrimSpace(string(byteArr)) + "/tests/"
}
var loggedSQL string
var loggedSQLArgs []interface{}
var loggedDebugSQL string
func init() {
sqlite.SetLogger(func(ctx context.Context, statement sqlite.PrintableStatement) {
loggedSQL, loggedSQLArgs = statement.Sql()
loggedDebugSQL = statement.DebugSql()
})
}
func requireLogged(t *testing.T, statement sqlite.Statement) {
query, args := statement.Sql()
require.Equal(t, loggedSQL, query)
require.Equal(t, loggedSQLArgs, args)
require.Equal(t, loggedDebugSQL, statement.DebugSql())
}
func beginSampleDBTx(t *testing.T) *sql.Tx {
tx, err := sampleDB.Begin()
require.NoError(t, err)
return tx
}
func beginDBTx(t *testing.T) *sql.Tx {
tx, err := db.Begin()
require.NoError(t, err)
return tx
}

View file

@ -0,0 +1,121 @@
package sqlite
import (
"context"
"testing"
"time"
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/sakila/model"
"github.com/stretchr/testify/require"
)
func TestRawStatementSelect(t *testing.T) {
stmt := RawStatement(`
SELECT actor.first_name AS "actor.first_name"
FROM actor
WHERE actor.actor_id = 2`)
testutils.AssertStatementSql(t, stmt, `
SELECT actor.first_name AS "actor.first_name"
FROM actor
WHERE actor.actor_id = 2;
`)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT actor.first_name AS "actor.first_name"
FROM actor
WHERE actor.actor_id = 2;
`)
var actor model.Actor
err := stmt.Query(db, &actor)
require.NoError(t, err)
require.Equal(t, actor.FirstName, "NICK")
}
func TestRawStatementSelectWithArguments(t *testing.T) {
stmt := RawStatement(`
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM actor
WHERE actor.actor_id IN (#actorID1, #actorID2, #actorID3) AND ((#actorID1 / #actorID2) <> (#actorID2 * #actorID3))
ORDER BY actor.actor_id`,
RawArgs{
"#actorID1": int64(1),
"#actorID2": int64(2),
"#actorID3": int64(3),
},
)
testutils.AssertStatementSql(t, stmt, `
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM actor
WHERE actor.actor_id IN (?, ?, ?) AND ((? / ?) <> (? * ?))
ORDER BY actor.actor_id;
`, int64(1), int64(2), int64(3), int64(1), int64(2), int64(2), int64(3))
testutils.AssertDebugStatementSql(t, stmt, `
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM actor
WHERE actor.actor_id IN (1, 2, 3) AND ((1 / 2) <> (2 * 3))
ORDER BY actor.actor_id;
`)
var actor []model.Actor
err := stmt.Query(db, &actor)
require.NoError(t, err)
testutils.AssertDeepEqual(t, actor[1], model.Actor{
ActorID: 2,
FirstName: "NICK",
LastName: "WAHLBERG",
LastUpdate: *testutils.TimestampWithoutTimeZone("2019-04-11 18:11:48", 2),
})
}
func TestRawStatementRows(t *testing.T) {
stmt := RawStatement(`
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM actor
ORDER BY actor.actor_id`)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
for rows.Next() {
var actor model.Actor
err := rows.Scan(&actor)
require.NoError(t, err)
require.NotEqual(t, actor.ActorID, int16(0))
require.NotEqual(t, actor.FirstName, "")
require.NotEqual(t, actor.LastName, "")
require.NotEqual(t, actor.LastUpdate, time.Time{})
if actor.ActorID == 54 {
require.Equal(t, actor.ActorID, int32(54))
require.Equal(t, actor.FirstName, "PENELOPE")
require.Equal(t, actor.LastName, "PINKETT")
require.Equal(t, actor.LastUpdate.Format(time.RFC3339), "2019-04-11T18:11:48Z")
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
requireLogged(t, stmt)
}

749
tests/sqlite/select_test.go Normal file
View file

@ -0,0 +1,749 @@
package sqlite
import (
"context"
model2 "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/chinook/model"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/chinook/table"
"strings"
"testing"
"time"
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/sakila/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/sakila/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/sakila/view"
"github.com/stretchr/testify/require"
)
func TestSelect_ScanToStruct(t *testing.T) {
query := Actor.
SELECT(Actor.AllColumns).
DISTINCT().
WHERE(Actor.ActorID.EQ(Int(2)))
testutils.AssertStatementSql(t, query, `
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM actor
WHERE actor.actor_id = ?;
`, int64(2))
actor := model.Actor{}
err := query.Query(db, &actor)
require.NoError(t, err)
testutils.AssertDeepEqual(t, actor, actor2)
requireLogged(t, query)
}
var actor2 = model.Actor{
ActorID: 2,
FirstName: "NICK",
LastName: "WAHLBERG",
LastUpdate: *testutils.TimestampWithoutTimeZone("2019-04-11 18:11:48", 2),
}
func TestSelect_ScanToSlice(t *testing.T) {
query := SELECT(Actor.AllColumns).
FROM(Actor).
ORDER_BY(Actor.ActorID)
testutils.AssertStatementSql(t, query, `
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM actor
ORDER BY actor.actor_id;
`)
dest := []model.Actor{}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 200)
testutils.AssertDeepEqual(t, dest[1], actor2)
//testutils.SaveJSONFile(dest, "./testdata/results/sqlite/all_actors.json")
testutils.AssertJSONFile(t, dest, "./testdata/results/sqlite/all_actors.json")
requireLogged(t, query)
}
func TestSelectGroupByHaving(t *testing.T) {
expectedSQL := `
SELECT customer.customer_id AS "customer.customer_id",
customer.store_id AS "customer.store_id",
customer.first_name AS "customer.first_name",
customer.last_name AS "customer.last_name",
customer.email AS "customer.email",
customer.address_id AS "customer.address_id",
customer.active AS "customer.active",
customer.create_date AS "customer.create_date",
customer.last_update AS "customer.last_update",
SUM(payment.amount) AS "amount.sum",
AVG(payment.amount) AS "amount.avg",
MAX(payment.payment_date) AS "amount.max_date",
MAX(payment.amount) AS "amount.max",
MIN(payment.payment_date) AS "amount.min_date",
MIN(payment.amount) AS "amount.min",
COUNT(payment.amount) AS "amount.count"
FROM payment
INNER JOIN customer ON (customer.customer_id = payment.customer_id)
GROUP BY payment.customer_id
HAVING SUM(payment.amount) > 125.6
ORDER BY payment.customer_id, SUM(payment.amount) ASC;
`
query := Payment.
INNER_JOIN(Customer, Customer.CustomerID.EQ(Payment.CustomerID)).
SELECT(
Customer.AllColumns,
SUMf(Payment.Amount).AS("amount.sum"),
AVG(Payment.Amount).AS("amount.avg"),
MAX(Payment.PaymentDate).AS("amount.max_date"),
MAXf(Payment.Amount).AS("amount.max"),
MIN(Payment.PaymentDate).AS("amount.min_date"),
MINf(Payment.Amount).AS("amount.min"),
COUNT(Payment.Amount).AS("amount.count"),
).
GROUP_BY(Payment.CustomerID).
HAVING(
SUMf(Payment.Amount).GT(Float(125.6)),
).
ORDER_BY(
Payment.CustomerID, SUMf(Payment.Amount).ASC(),
)
testutils.AssertDebugStatementSql(t, query, expectedSQL, float64(125.6))
var dest []struct {
model.Customer
Amount struct {
Sum float64
Avg float64
Max float64
Min float64
Count int64
} `alias:"amount"`
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 174)
//testutils.SaveJSONFile(dest, "./testdata/results/sqlite/customer_payment_sum.json")
testutils.AssertJSONFile(t, dest, "./testdata/results/sqlite/customer_payment_sum.json")
requireLogged(t, query)
}
func TestSubQuery(t *testing.T) {
rRatingFilms :=
SELECT(
Film.FilmID,
Film.Title,
Film.Rating,
).FROM(
Film,
).WHERE(Film.Rating.EQ(String("R"))).
AsTable("rFilms")
rFilmID := Film.FilmID.From(rRatingFilms)
main :=
SELECT(
Actor.AllColumns,
FilmActor.AllColumns,
rRatingFilms.AllColumns(),
).FROM(
rRatingFilms.
INNER_JOIN(FilmActor, FilmActor.FilmID.EQ(rFilmID)).
INNER_JOIN(Actor, Actor.ActorID.EQ(FilmActor.ActorID)),
).ORDER_BY(
rFilmID,
Actor.ActorID,
)
var dest []struct {
model.Film
Actors []model.Actor
}
err := main.Query(db, &dest)
require.NoError(t, err)
//testutils.SaveJSONFile(dest, "./testdata/results/sqlite/r_rating_films.json")
testutils.AssertJSONFile(t, dest, "./testdata/results/sqlite/r_rating_films.json")
}
func TestSelectAndUnionInProjection(t *testing.T) {
query := UNION(
SELECT(
Payment.PaymentID,
).FROM(Payment),
SELECT(
STAR,
).FROM(
SELECT(Payment.PaymentID).
FROM(Payment).LIMIT(1).OFFSET(2).AsTable("p"),
),
).LIMIT(1).OFFSET(10)
testutils.AssertDebugStatementSql(t, query, `
SELECT payment.payment_id AS "payment.payment_id"
FROM payment
UNION
SELECT *
FROM (
SELECT payment.payment_id AS "payment.payment_id"
FROM payment
LIMIT 1
OFFSET 2
) AS p
LIMIT 1
OFFSET 10;
`, int64(1), int64(2), int64(1), int64(10))
dest := []struct{}{}
err := query.Query(db, &dest)
require.NoError(t, err)
}
func TestSelectUNION(t *testing.T) {
expectedSQL := `
SELECT payment.payment_id AS "payment.payment_id"
FROM payment
WHERE payment.payment_id > ?
UNION
SELECT payment.payment_id AS "payment.payment_id"
FROM payment
WHERE payment.amount < ?
LIMIT ?;
`
query := UNION(
SELECT(Payment.PaymentID).
FROM(Payment).
WHERE(Payment.PaymentID.GT(Int(11))),
SELECT(Payment.PaymentID).
FROM(Payment).
WHERE(Payment.Amount.LT(Float(2000.0))),
).LIMIT(1)
testutils.AssertStatementSql(t, query, expectedSQL, int64(11), 2000.0, int64(1))
query2 :=
SELECT(
Payment.PaymentID,
).FROM(
Payment,
).WHERE(
Payment.PaymentID.GT(Int(11)),
).UNION(
SELECT(Payment.PaymentID).
FROM(Payment).
WHERE(Payment.Amount.LT(Float(2000.0))),
).LIMIT(1)
testutils.AssertStatementSql(t, query2, expectedSQL, int64(11), 2000.0, int64(1))
dest := []struct{}{}
err := query.Query(db, &dest)
require.NoError(t, err)
}
func TestSelectUNION_ALL(t *testing.T) {
expectedSQL := `
SELECT payment.payment_id AS "payment.payment_id"
FROM payment
WHERE payment.payment_id > ?
UNION ALL
SELECT payment.payment_id AS "payment.payment_id"
FROM payment
WHERE payment.amount < ?
LIMIT ?;
`
query := UNION_ALL(
SELECT(Payment.PaymentID).
FROM(Payment).
WHERE(Payment.PaymentID.GT(Int(11))),
SELECT(Payment.PaymentID).
FROM(Payment).
WHERE(Payment.Amount.LT(Float(2000.0))),
).LIMIT(1)
testutils.AssertStatementSql(t, query, expectedSQL, int64(11), 2000.0, int64(1))
dest := []struct{}{}
err := query.Query(db, &dest)
require.NoError(t, err)
}
func TestJoinQueryStruct(t *testing.T) {
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",
film.film_id AS "film.film_id",
film.title AS "film.title",
film.description AS "film.description",
film.release_year AS "film.release_year",
film.language_id AS "film.language_id",
film.original_language_id AS "film.original_language_id",
film.rental_duration AS "film.rental_duration",
film.rental_rate AS "film.rental_rate",
film.length AS "film.length",
film.replacement_cost AS "film.replacement_cost",
film.rating AS "film.rating",
film.special_features AS "film.special_features",
film.last_update AS "film.last_update",
language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update",
actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update",
inventory.inventory_id AS "inventory.inventory_id",
inventory.film_id AS "inventory.film_id",
inventory.store_id AS "inventory.store_id",
inventory.last_update AS "inventory.last_update",
rental.rental_id AS "rental.rental_id",
rental.rental_date AS "rental.rental_date",
rental.inventory_id AS "rental.inventory_id",
rental.customer_id AS "rental.customer_id",
rental.return_date AS "rental.return_date",
rental.staff_id AS "rental.staff_id",
rental.last_update AS "rental.last_update"
FROM language
INNER JOIN film ON (film.language_id = language.language_id)
INNER JOIN film_actor ON (film_actor.film_id = film.film_id)
INNER JOIN actor ON (actor.actor_id = film_actor.actor_id)
LEFT JOIN inventory ON (inventory.film_id = film.film_id)
LEFT JOIN rental ON (rental.inventory_id = inventory.inventory_id)
ORDER BY language.language_id ASC, film.film_id ASC, actor.actor_id ASC, inventory.inventory_id ASC, rental.rental_id ASC
LIMIT ?;
`
for i := 0; i < 2; i++ {
query :=
SELECT(
FilmActor.AllColumns,
Film.AllColumns,
Language.AllColumns,
Actor.AllColumns,
Inventory.AllColumns,
Rental.AllColumns,
).
FROM(
Language.
INNER_JOIN(Film, Film.LanguageID.EQ(Language.LanguageID)).
INNER_JOIN(FilmActor, FilmActor.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Actor, Actor.ActorID.EQ(FilmActor.ActorID)).
LEFT_JOIN(Inventory, Inventory.FilmID.EQ(Film.FilmID)).
LEFT_JOIN(Rental, Rental.InventoryID.EQ(Inventory.InventoryID)),
).ORDER_BY(
Language.LanguageID.ASC(),
Film.FilmID.ASC(),
Actor.ActorID.ASC(),
Inventory.InventoryID.ASC(),
Rental.RentalID.ASC(),
).
LIMIT(1000)
testutils.AssertStatementSql(t, query, expectedSQL, int64(1000))
var dest []struct {
model.Language
Films []struct {
model.Film
Actors []struct {
model.Actor
}
Inventories *[]struct {
model.Inventory
Rentals *[]model.Rental
}
}
}
err := query.Query(db, &dest)
require.NoError(t, err)
testutils.AssertJSONFile(t, dest, "./testdata/results/sqlite/lang_film_actor_inventory_rental.json")
}
}
func TestExpressionWrappers(t *testing.T) {
query := SELECT(
BoolExp(Raw("true")),
IntExp(Raw("11")),
FloatExp(Raw("11.22")),
StringExp(Raw("'stringer'")),
TimeExp(Raw("'raw'")),
TimestampExp(Raw("'raw'")),
DateTimeExp(Raw("'raw'")),
DateExp(Raw("'date'")),
)
testutils.AssertStatementSql(t, query, `
SELECT true,
11,
11.22,
'stringer',
'raw',
'raw',
'raw',
'date';
`)
dest := []struct{}{}
err := query.Query(db, &dest)
require.NoError(t, err)
}
func TestWindowFunction(t *testing.T) {
var expectedSQL = `
SELECT AVG(payment.amount) OVER (),
AVG(payment.amount) OVER (PARTITION BY payment.customer_id),
MAX(payment.amount) OVER (ORDER BY payment.payment_date DESC),
MIN(payment.amount) OVER (PARTITION BY payment.customer_id ORDER BY payment.payment_date DESC),
SUM(payment.amount) OVER (PARTITION BY payment.customer_id ORDER BY payment.payment_date DESC ROWS BETWEEN 1 PRECEDING AND 6 FOLLOWING),
SUM(payment.amount) OVER (PARTITION BY payment.customer_id ORDER BY payment.payment_date DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),
MAX(payment.customer_id) OVER (ORDER BY payment.payment_date DESC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING),
MIN(payment.customer_id) OVER (PARTITION BY payment.customer_id ORDER BY payment.payment_date DESC),
SUM(payment.customer_id) OVER (PARTITION BY payment.customer_id ORDER BY payment.payment_date DESC),
ROW_NUMBER() OVER (ORDER BY payment.payment_date),
RANK() OVER (ORDER BY payment.payment_date),
DENSE_RANK() OVER (ORDER BY payment.payment_date),
CUME_DIST() OVER (ORDER BY payment.payment_date),
NTILE(11) OVER (ORDER BY payment.payment_date),
LAG(payment.amount) OVER (ORDER BY payment.payment_date),
LAG(payment.amount) OVER (ORDER BY payment.payment_date),
LAG(payment.amount, 2, payment.amount) OVER (ORDER BY payment.payment_date),
LAG(payment.amount, 2, ?) OVER (ORDER BY payment.payment_date),
LEAD(payment.amount) OVER (ORDER BY payment.payment_date),
LEAD(payment.amount) OVER (ORDER BY payment.payment_date),
LEAD(payment.amount, 2, payment.amount) OVER (ORDER BY payment.payment_date),
LEAD(payment.amount, 2, ?) OVER (ORDER BY payment.payment_date),
FIRST_VALUE(payment.amount) OVER (ORDER BY payment.payment_date),
LAST_VALUE(payment.amount) OVER (ORDER BY payment.payment_date),
NTH_VALUE(payment.amount, 3) OVER (ORDER BY payment.payment_date)
FROM payment
WHERE payment.payment_id < ?
GROUP BY payment.amount, payment.customer_id, payment.payment_date;
`
query :=
SELECT(
AVG(Payment.Amount).OVER(),
AVG(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID)),
MAXf(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate.DESC())),
MINf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
SUMf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).
ORDER_BY(Payment.PaymentDate.DESC()).ROWS(PRECEDING(1), FOLLOWING(6))),
SUMf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).
ORDER_BY(Payment.PaymentDate.DESC()).RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED))),
MAXi(Payment.CustomerID).OVER(ORDER_BY(Payment.PaymentDate.DESC()).ROWS(CURRENT_ROW, FOLLOWING(UNBOUNDED))),
MINi(Payment.CustomerID).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
SUMi(Payment.CustomerID).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())),
ROW_NUMBER().OVER(ORDER_BY(Payment.PaymentDate)),
RANK().OVER(ORDER_BY(Payment.PaymentDate)),
DENSE_RANK().OVER(ORDER_BY(Payment.PaymentDate)),
CUME_DIST().OVER(ORDER_BY(Payment.PaymentDate)),
NTILE(11).OVER(ORDER_BY(Payment.PaymentDate)),
LAG(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
LAG(Payment.Amount, 2).OVER(ORDER_BY(Payment.PaymentDate)),
LAG(Payment.Amount, 2, Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
LAG(Payment.Amount, 2, 100).OVER(ORDER_BY(Payment.PaymentDate)),
LEAD(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
LEAD(Payment.Amount, 2).OVER(ORDER_BY(Payment.PaymentDate)),
LEAD(Payment.Amount, 2, Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
LEAD(Payment.Amount, 2, 100).OVER(ORDER_BY(Payment.PaymentDate)),
FIRST_VALUE(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
LAST_VALUE(Payment.Amount).OVER(ORDER_BY(Payment.PaymentDate)),
NTH_VALUE(Payment.Amount, 3).OVER(ORDER_BY(Payment.PaymentDate)),
).FROM(
Payment,
).GROUP_BY(
Payment.Amount,
Payment.CustomerID,
Payment.PaymentDate,
).WHERE(Payment.PaymentID.LT(Int(10)))
testutils.AssertStatementSql(t, query, expectedSQL, 100, 100, int64(10))
dest := []struct{}{}
err := query.Query(db, &dest)
require.NoError(t, err)
}
func TestWindowClause(t *testing.T) {
var expectedSQL = `
SELECT AVG(payment.amount) OVER (),
AVG(payment.amount) OVER (w1),
AVG(payment.amount) OVER (w2 ORDER BY payment.customer_id RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),
AVG(payment.amount) OVER (w3 RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM payment
WHERE payment.payment_id < ?
WINDOW w1 AS (PARTITION BY payment.payment_date), w2 AS (w1), w3 AS (w2 ORDER BY payment.customer_id)
ORDER BY payment.customer_id;
`
query := SELECT(
AVG(Payment.Amount).OVER(),
AVG(Payment.Amount).OVER(Window("w1")),
AVG(Payment.Amount).OVER(
Window("w2").
ORDER_BY(Payment.CustomerID).
RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED)),
),
AVG(Payment.Amount).OVER(Window("w3").RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED))),
).FROM(
Payment,
).WHERE(
Payment.PaymentID.LT(Int(10)),
).
WINDOW("w1").AS(PARTITION_BY(Payment.PaymentDate)).
WINDOW("w2").AS(Window("w1")).
WINDOW("w3").AS(Window("w2").ORDER_BY(Payment.CustomerID)).
ORDER_BY(
Payment.CustomerID,
)
testutils.AssertStatementSql(t, query, expectedSQL, int64(10))
dest := []struct{}{}
err := query.Query(db, &dest)
require.NoError(t, err)
}
func TestSimpleView(t *testing.T) {
query :=
SELECT(
view.CustomerList.AllColumns,
).FROM(
view.CustomerList,
).ORDER_BY(
view.CustomerList.ID,
).LIMIT(10)
var dest []model.CustomerList
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 10)
require.Equal(t, dest[2], model.CustomerList{
ID: testutils.Int32Ptr(3),
Name: testutils.StringPtr("LINDA WILLIAMS"),
Address: testutils.StringPtr("692 Joliet Street"),
ZipCode: testutils.StringPtr("83579"),
Phone: testutils.StringPtr(" "),
City: testutils.StringPtr("Athenai"),
Country: testutils.StringPtr("Greece"),
Notes: testutils.StringPtr("active"),
Sid: testutils.Int32Ptr(1),
})
}
func TestJoinViewWithTable(t *testing.T) {
query :=
SELECT(
view.CustomerList.AllColumns,
Rental.AllColumns,
).FROM(
view.CustomerList.
INNER_JOIN(Rental, view.CustomerList.ID.EQ(Rental.CustomerID)),
).ORDER_BY(
view.CustomerList.ID,
).WHERE(
view.CustomerList.ID.LT_EQ(Int(2)),
)
var dest []struct {
model.CustomerList `sql:"primary_key=ID"`
Rentals []model.Rental
}
err := query.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 2)
require.Equal(t, len(dest[0].Rentals), 32)
require.Equal(t, len(dest[1].Rentals), 27)
}
func TestConditionalProjectionList(t *testing.T) {
projectionList := ProjectionList{}
columnsToSelect := []string{"customer_id", "create_date"}
for _, columnName := range columnsToSelect {
switch columnName {
case Customer.CustomerID.Name():
projectionList = append(projectionList, Customer.CustomerID)
case Customer.Email.Name():
projectionList = append(projectionList, Customer.Email)
case Customer.CreateDate.Name():
projectionList = append(projectionList, Customer.CreateDate)
}
}
stmt := SELECT(projectionList).
FROM(Customer).
LIMIT(3)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT customer.customer_id AS "customer.customer_id",
customer.create_date AS "customer.create_date"
FROM customer
LIMIT 3;
`)
var dest []model.Customer
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 3)
}
func TestUseAttachedDatabase(t *testing.T) {
Artists := table.Artists.FromSchema("chinook")
Albums := table.Albums.FromSchema("chinook")
stmt :=
SELECT(
Artists.AllColumns,
Albums.AllColumns,
).FROM(
Albums.
INNER_JOIN(Artists, Artists.ArtistId.EQ(Albums.ArtistId)),
).ORDER_BY(
Artists.ArtistId,
).LIMIT(10)
testutils.AssertDebugStatementSql(t, stmt, strings.Replace(`
SELECT artists.''ArtistId'' AS "artists.ArtistId",
artists.''Name'' AS "artists.Name",
albums.''AlbumId'' AS "albums.AlbumId",
albums.''Title'' AS "albums.Title",
albums.''ArtistId'' AS "albums.ArtistId"
FROM chinook.albums
INNER JOIN chinook.artists ON (artists.''ArtistId'' = albums.''ArtistId'')
ORDER BY artists.''ArtistId''
LIMIT 10;
`, "''", "`", -1))
var dest []struct {
model2.Artists
Albums []model2.Albums
}
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Len(t, dest, 7)
}
func TestRowsScan(t *testing.T) {
stmt :=
SELECT(
Inventory.AllColumns,
).FROM(
Inventory,
).ORDER_BY(
Inventory.InventoryID.ASC(),
)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
for rows.Next() {
var inventory model.Inventory
err = rows.Scan(&inventory)
require.NoError(t, err)
require.NotEqual(t, inventory.InventoryID, uint32(0))
require.NotEqual(t, inventory.FilmID, uint16(0))
require.NotEqual(t, inventory.StoreID, uint16(0))
require.NotEqual(t, inventory.LastUpdate, time.Time{})
if inventory.InventoryID == 2103 {
require.Equal(t, inventory.FilmID, int32(456))
require.Equal(t, inventory.StoreID, int32(2))
require.Equal(t, inventory.LastUpdate.Format(time.RFC3339), "2019-04-11T18:11:48Z")
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
requireLogged(t, stmt)
}
func TestScanNumericToNumber(t *testing.T) {
type Number struct {
Int8 int8
UInt8 uint8
Int16 int16
UInt16 uint16
Int32 int32
UInt32 uint32
Int64 int64
UInt64 uint64
Float32 float32
Float64 float64
}
numeric := CAST(String("1234567890.111")).AS_REAL()
stmt := SELECT(
numeric.AS("number.int8"),
numeric.AS("number.uint8"),
numeric.AS("number.int16"),
numeric.AS("number.uint16"),
numeric.AS("number.int32"),
numeric.AS("number.uint32"),
numeric.AS("number.int64"),
numeric.AS("number.uint64"),
numeric.AS("number.float32"),
numeric.AS("number.float64"),
)
var number Number
err := stmt.Query(db, &number)
require.NoError(t, err)
require.Equal(t, number.Int8, int8(-46)) // overflow
require.Equal(t, number.UInt8, uint8(210)) // overflow
require.Equal(t, number.Int16, int16(722)) // overflow
require.Equal(t, number.UInt16, uint16(722)) // overflow
require.Equal(t, number.Int32, int32(1234567890))
require.Equal(t, number.UInt32, uint32(1234567890))
require.Equal(t, number.Int64, int64(1234567890))
require.Equal(t, number.UInt64, uint64(1234567890))
require.Equal(t, number.Float32, float32(1.234568e+09))
require.Equal(t, number.Float64, float64(1.234567890111e+09))
}

290
tests/sqlite/update_test.go Normal file
View file

@ -0,0 +1,290 @@
package sqlite
import (
"context"
"testing"
"time"
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
"github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/test_sample/table"
"github.com/stretchr/testify/require"
)
func TestUpdateValues(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
var expectedSQL = `
UPDATE link
SET name = 'Bong',
url = 'http://bong.com'
WHERE link.name = 'Bing';
`
t.Run("old version", func(t *testing.T) {
query := Link.UPDATE(Link.Name, Link.URL).
SET("Bong", "http://bong.com").
WHERE(Link.Name.EQ(String("Bing")))
testutils.AssertDebugStatementSql(t, query, expectedSQL, "Bong", "http://bong.com", "Bing")
testutils.AssertExec(t, query, tx)
requireLogged(t, query)
})
t.Run("new version", func(t *testing.T) {
stmt := Link.UPDATE().
SET(
Link.Name.SET(String("Bong")),
Link.URL.SET(String("http://bong.com")),
).
WHERE(Link.Name.EQ(String("Bing")))
testutils.AssertDebugStatementSql(t, stmt, expectedSQL, "Bong", "http://bong.com", "Bing")
testutils.AssertExec(t, stmt, tx)
requireLogged(t, stmt)
})
links := []model.Link{}
err := SELECT(Link.AllColumns).
FROM(Link).
WHERE(Link.Name.EQ(String("Bong"))).
Query(tx, &links)
require.NoError(t, err)
require.Equal(t, len(links), 1)
testutils.AssertDeepEqual(t, links[0], model.Link{
ID: 24,
URL: "http://bong.com",
Name: "Bong",
})
}
func TestUpdateWithSubQueries(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
expectedSQL := `
UPDATE link
SET name = ?,
url = (
SELECT link.url AS "link.url"
FROM link
WHERE link.name = ?
)
WHERE link.name = ?;
`
t.Run("old version", func(t *testing.T) {
query := Link.
UPDATE(Link.Name, Link.URL).
SET(
String("Bong"),
SELECT(Link.URL).
FROM(Link).
WHERE(Link.Name.EQ(String("Ask"))),
).
WHERE(Link.Name.EQ(String("Bing")))
testutils.AssertStatementSql(t, query, expectedSQL, "Bong", "Ask", "Bing")
testutils.AssertExec(t, query, tx)
requireLogged(t, query)
})
t.Run("new version", func(t *testing.T) {
query := Link.
UPDATE().
SET(
Link.Name.SET(String("Bong")),
Link.URL.SET(StringExp(
SELECT(Link.URL).
FROM(Link).
WHERE(Link.Name.EQ(String("Ask"))),
)),
).
WHERE(Link.Name.EQ(String("Bing")))
testutils.AssertStatementSql(t, query, expectedSQL, "Bong", "Ask", "Bing")
testutils.AssertExec(t, query, tx)
requireLogged(t, query)
})
}
func TestUpdateWithModelDataAndReturning(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
link := model.Link{
ID: 20,
URL: "http://www.duckduckgo.com",
Name: "DuckDuckGo",
}
stmt := Link.UPDATE(Link.AllColumns).
MODEL(link).
WHERE(Link.ID.EQ(Int32(link.ID))).
RETURNING(
Link.AllColumns,
String("str").AS("dest.literal"),
NOT(Bool(false)).AS("dest.unary_operator"),
Link.ID.ADD(Int(11)).AS("dest.binary_operator"),
CAST(Link.ID).AS_TEXT().AS("dest.cast_operator"),
Link.Name.LIKE(String("Bing")).AS("dest.like_operator"),
Link.Description.IS_NULL().AS("dest.is_null"),
CASE(Link.Name).
WHEN(String("Yahoo")).THEN(String("search")).
WHEN(String("GMail")).THEN(String("mail")).
ELSE(String("unknown")).AS("dest.case_operator"),
)
expectedSQL := `
UPDATE link
SET id = ?,
url = ?,
name = ?,
description = ?
WHERE link.id = ?
RETURNING link.id AS "link.id",
link.url AS "link.url",
link.name AS "link.name",
link.description AS "link.description",
? AS "dest.literal",
(NOT ?) AS "dest.unary_operator",
(link.id + ?) AS "dest.binary_operator",
CAST(link.id AS TEXT) AS "dest.cast_operator",
(link.name LIKE ?) AS "dest.like_operator",
link.description IS NULL AS "dest.is_null",
(CASE link.name WHEN ? THEN ? WHEN ? THEN ? ELSE ? END) AS "dest.case_operator";
`
testutils.AssertStatementSql(t, stmt, expectedSQL, int32(20), "http://www.duckduckgo.com", "DuckDuckGo", nil, int32(20),
"str", false, int64(11), "Bing", "Yahoo", "search", "GMail", "mail", "unknown")
type Dest struct {
model.Link
Literal string
UnaryOperator bool
BinaryOperator int64
CastOperator string
LikeOperator bool
IsNull bool
CaseOperator string
}
var dest Dest
err := stmt.Query(tx, &dest)
require.NoError(t, err)
require.EqualValues(t, dest, Dest{
Link: link,
Literal: "str",
UnaryOperator: true,
BinaryOperator: 31,
CastOperator: "20",
LikeOperator: false,
IsNull: true,
CaseOperator: "unknown",
})
requireLogged(t, stmt)
}
func TestUpdateWithModelDataAndPredefinedColumnList(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
link := model.Link{
ID: 20,
URL: "http://www.duckduckgo.com",
Name: "DuckDuckGo",
}
updateColumnList := ColumnList{Link.Description, Link.Name, Link.URL}
stmt := Link.UPDATE(updateColumnList).
MODEL(link).
WHERE(Link.ID.EQ(Int32(link.ID)))
var expectedSQL = `
UPDATE link
SET description = NULL,
name = 'DuckDuckGo',
url = 'http://www.duckduckgo.com'
WHERE link.id = 20;
`
testutils.AssertDebugStatementSql(t, stmt, expectedSQL, nil, "DuckDuckGo", "http://www.duckduckgo.com", int32(20))
testutils.AssertExec(t, stmt, tx)
requireLogged(t, stmt)
}
func TestUpdateWithModelDataAndMutableColumns(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
link := model.Link{
ID: 201,
URL: "http://www.duckduckgo.com",
Name: "DuckDuckGo",
}
stmt := Link.UPDATE(Link.MutableColumns).
MODEL(link).
WHERE(Link.ID.EQ(Int32(link.ID)))
var expectedSQL = `
UPDATE link
SET url = 'http://www.duckduckgo.com',
name = 'DuckDuckGo',
description = NULL
WHERE link.id = 201;
`
testutils.AssertDebugStatementSql(t, stmt, expectedSQL, "http://www.duckduckgo.com", "DuckDuckGo", nil, int32(201))
testutils.AssertExec(t, stmt, tx)
}
func TestUpdateWithInvalidModelData(t *testing.T) {
defer func() {
r := recover()
require.Equal(t, r, "missing struct field for column : id")
}()
link := struct {
Ident int
URL string
Name string
Description *string
Rel *string
}{
Ident: 201,
URL: "http://www.duckduckgo.com",
Name: "DuckDuckGo",
}
stmt := Link.UPDATE(Link.AllColumns).
MODEL(link).
WHERE(Link.ID.EQ(Int(int64(link.Ident))))
stmt.Sql()
}
func TestUpdateContextDeadlineExceeded(t *testing.T) {
tx := beginSampleDBTx(t)
defer tx.Rollback()
updateStmt := Link.UPDATE(Link.Name, Link.URL).
SET("Bong", "http://bong.com").
WHERE(Link.Name.EQ(String("Bing")))
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond)
defer cancel()
time.Sleep(10 * time.Millisecond)
dest := []model.Link{}
err := updateStmt.QueryContext(ctx, tx, &dest)
require.Error(t, err, "context deadline exceeded")
_, err = updateStmt.ExecContext(ctx, tx)
require.Error(t, err, "context deadline exceeded")
}

234
tests/sqlite/with_test.go Normal file
View file

@ -0,0 +1,234 @@
package sqlite
import (
"github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/sqlite"
. "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/sakila/table"
"github.com/stretchr/testify/require"
"strings"
"testing"
)
func TestWITH_And_SELECT(t *testing.T) {
salesRep := CTE("sales_rep")
salesRepStaffID := Staff.StaffID.From(salesRep)
salesRepFullName := StringColumn("sales_rep_full_name").From(salesRep)
customerSalesRep := CTE("customer_sales_rep")
stmt := WITH(
salesRep.AS(
SELECT(
Staff.StaffID,
Staff.FirstName.CONCAT(Staff.LastName).AS(salesRepFullName.Name()),
).FROM(Staff),
),
customerSalesRep.AS(
SELECT(
Customer.FirstName.CONCAT(Customer.LastName).AS("customer_name"),
salesRepFullName,
).FROM(
salesRep.
INNER_JOIN(Store, Store.ManagerStaffID.EQ(salesRepStaffID)).
INNER_JOIN(Customer, Customer.StoreID.EQ(Store.StoreID)),
),
),
)(
SELECT(customerSalesRep.AllColumns()).
FROM(customerSalesRep),
)
testutils.AssertStatementSql(t, stmt, strings.Replace(`
WITH sales_rep AS (
SELECT staff.staff_id AS "staff.staff_id",
(staff.first_name || staff.last_name) AS "sales_rep_full_name"
FROM staff
),customer_sales_rep AS (
SELECT (customer.first_name || customer.last_name) AS "customer_name",
sales_rep.sales_rep_full_name AS "sales_rep_full_name"
FROM sales_rep
INNER JOIN store ON (store.manager_staff_id = sales_rep.''staff.staff_id'')
INNER JOIN customer ON (customer.store_id = store.store_id)
)
SELECT customer_sales_rep.customer_name AS "customer_name",
customer_sales_rep.sales_rep_full_name AS "sales_rep_full_name"
FROM customer_sales_rep;
`, "''", "`", -1))
var dest []struct {
CustomerName string
SalesRepFullName string
}
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, len(dest), 599)
}
func TestWITH_And_INSERT(t *testing.T) {
paymentsToInsert := CTE("payments_to_insert")
stmt := WITH(
paymentsToInsert.AS(
SELECT(Payment.AllColumns).
FROM(Payment).
WHERE(Payment.Amount.LT(Float(0.5))),
),
)(
Payment.INSERT(Payment.AllColumns).
QUERY(
SELECT(
paymentsToInsert.AllColumns(),
).FROM(
paymentsToInsert,
).WHERE(Bool(true)), //https://stackoverflow.com/questions/66230093/error-while-doing-upsert-in-sqlite-3-34-error-near-do-syntax-error
).ON_CONFLICT().DO_UPDATE(
SET(
Payment.PaymentID.SET(Payment.PaymentID.ADD(Int(100000))),
),
),
)
testutils.AssertDebugStatementSql(t, stmt, strings.Replace(`
WITH payments_to_insert AS (
SELECT payment.payment_id AS "payment.payment_id",
payment.customer_id AS "payment.customer_id",
payment.staff_id AS "payment.staff_id",
payment.rental_id AS "payment.rental_id",
payment.amount AS "payment.amount",
payment.payment_date AS "payment.payment_date",
payment.last_update AS "payment.last_update"
FROM payment
WHERE payment.amount < 0.5
)
INSERT INTO payment (payment_id, customer_id, staff_id, rental_id, amount, payment_date, last_update)
SELECT payments_to_insert.''payment.payment_id'' AS "payment.payment_id",
payments_to_insert.''payment.customer_id'' AS "payment.customer_id",
payments_to_insert.''payment.staff_id'' AS "payment.staff_id",
payments_to_insert.''payment.rental_id'' AS "payment.rental_id",
payments_to_insert.''payment.amount'' AS "payment.amount",
payments_to_insert.''payment.payment_date'' AS "payment.payment_date",
payments_to_insert.''payment.last_update'' AS "payment.last_update"
FROM payments_to_insert
WHERE TRUE
ON CONFLICT DO UPDATE
SET payment_id = (payment.payment_id + 100000);
`, "''", "`", -1))
tx := beginDBTx(t)
defer tx.Rollback()
testutils.AssertExec(t, stmt, tx, 24)
}
func TestWITH_SELECT_UPDATE(t *testing.T) {
paymentsToUpdate := CTE("payments_to_update")
paymentsToDeleteID := Payment.PaymentID.From(paymentsToUpdate)
stmt := WITH(
paymentsToUpdate.AS(
SELECT(Payment.AllColumns).
FROM(Payment).
WHERE(Payment.Amount.LT(Float(0.5))),
),
)(
Payment.UPDATE().
SET(Payment.Amount.SET(Float(0.0))).
WHERE(Payment.PaymentID.IN(
SELECT(paymentsToDeleteID).
FROM(paymentsToUpdate),
),
),
)
testutils.AssertDebugStatementSql(t, stmt, strings.Replace(`
WITH payments_to_update AS (
SELECT payment.payment_id AS "payment.payment_id",
payment.customer_id AS "payment.customer_id",
payment.staff_id AS "payment.staff_id",
payment.rental_id AS "payment.rental_id",
payment.amount AS "payment.amount",
payment.payment_date AS "payment.payment_date",
payment.last_update AS "payment.last_update"
FROM payment
WHERE payment.amount < 0.5
)
UPDATE payment
SET amount = 0
WHERE payment.payment_id IN (
SELECT payments_to_update.''payment.payment_id'' AS "payment.payment_id"
FROM payments_to_update
);
`, "''", "`", -1))
tx := beginDBTx(t)
defer tx.Rollback()
testutils.AssertExec(t, stmt, tx)
}
func TestWITH_And_DELETE(t *testing.T) {
paymentsToDelete := CTE("payments_to_delete")
paymentsToDeleteID := Payment.PaymentID.From(paymentsToDelete)
stmt := WITH(
paymentsToDelete.AS(
SELECT(
Payment.AllColumns,
).FROM(
Payment,
).WHERE(
Payment.Amount.LT(Float(0.5)),
),
),
)(
Payment.DELETE().
WHERE(
Payment.PaymentID.IN(
SELECT(
paymentsToDeleteID,
).FROM(
paymentsToDelete,
),
),
),
)
testutils.AssertDebugStatementSql(t, stmt, strings.Replace(`
WITH payments_to_delete AS (
SELECT payment.payment_id AS "payment.payment_id",
payment.customer_id AS "payment.customer_id",
payment.staff_id AS "payment.staff_id",
payment.rental_id AS "payment.rental_id",
payment.amount AS "payment.amount",
payment.payment_date AS "payment.payment_date",
payment.last_update AS "payment.last_update"
FROM payment
WHERE payment.amount < 0.5
)
DELETE FROM payment
WHERE payment.payment_id IN (
SELECT payments_to_delete.''payment.payment_id'' AS "payment.payment_id"
FROM payments_to_delete
);
`, "''", "`", -1))
tx := beginDBTx(t)
defer tx.Rollback()
testutils.AssertExec(t, stmt, tx, 24)
}
func TestOperatorIN(t *testing.T) {
stmt := SELECT(Payment.PaymentID.IN(SELECT(Int(11)), Int(22))).
FROM(Payment)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT payment.payment_id IN ((
SELECT 11
), 22)
FROM payment;
`)
var dest []struct{}
err := stmt.Query(db, &dest)
require.NoError(t, err)
}