Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed datetime and timestamp types to honor precision and default to 0 digit precision #1944

Merged
merged 18 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.19.x]
go-version: [1.20.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
Expand Down
6 changes: 5 additions & 1 deletion engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,11 @@ func bindingsToExprs(bindings map[string]*query.BindVariable) (map[string]sql.Ex
}
res[k] = expression.NewLiteral(v, t)
case v.Type() == sqltypes.Date || v.Type() == sqltypes.Datetime || v.Type() == sqltypes.Timestamp:
t, err := types.CreateDatetimeType(v.Type())
precision := 6
if v.Type() == sqltypes.Date {
precision = 0
}
t, err := types.CreateDatetimeType(v.Type(), precision)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ func TestBindingsToExprs(t *testing.T) {
"bit": expression.NewLiteral(uint64(0x0f), types.MustCreateBitType(types.BitTypeMaxBits)),
"date": expression.NewLiteral(time.Date(2020, time.Month(10), 20, 0, 0, 0, 0, time.UTC), types.Date),
"year": expression.NewLiteral(int16(2020), types.Year),
"datetime": expression.NewLiteral(time.Date(2020, time.Month(10), 20, 12, 0, 0, 0, time.UTC), types.Datetime),
"timestamp": expression.NewLiteral(time.Date(2020, time.Month(10), 20, 12, 0, 0, 0, time.UTC), types.Timestamp),
"datetime": expression.NewLiteral(time.Date(2020, time.Month(10), 20, 12, 0, 0, 0, time.UTC), types.MustCreateDatetimeType(query.Type_DATETIME, 6)),
"timestamp": expression.NewLiteral(time.Date(2020, time.Month(10), 20, 12, 0, 0, 0, time.UTC), types.MustCreateDatetimeType(query.Type_TIMESTAMP, 6)),
},
false,
},
Expand Down
10 changes: 5 additions & 5 deletions enginetest/enginetests.go
Original file line number Diff line number Diff line change
Expand Up @@ -2164,8 +2164,8 @@ func TestCreateTable(t *testing.T, harness Harness) {
{"val", "int", "YES", "", "NULL", ""}}, nil, nil)
})

t.Skip("primary key lengths are not stored properly")
for _, tt := range queries.BrokenCreateTableQueries {
t.Skip("primary key lengths are not stored properly")
RunWriteQueryTest(t, harness, tt)
}
}
Expand Down Expand Up @@ -5601,8 +5601,8 @@ func TestColumnDefaults(t *testing.T, harness Harness) {
e.Query(ctx, "set @@session.time_zone='SYSTEM';")
// TODO: NOW() and CURRENT_TIMESTAMP() are supposed to be the same function in MySQL, but we have two different
// implementations with slightly different behavior.
TestQueryWithContext(t, ctx, e, harness, "CREATE TABLE t10(pk BIGINT PRIMARY KEY, v1 DATETIME DEFAULT NOW(), v2 DATETIME DEFAULT CURRENT_TIMESTAMP(),"+
"v3 TIMESTAMP DEFAULT NOW(), v4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP())", []sql.Row{{types.NewOkResult(0)}}, nil, nil)
TestQueryWithContext(t, ctx, e, harness, "CREATE TABLE t10(pk BIGINT PRIMARY KEY, v1 DATETIME(6) DEFAULT NOW(), v2 DATETIME(6) DEFAULT CURRENT_TIMESTAMP(),"+
"v3 TIMESTAMP(6) DEFAULT NOW(), v4 TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP())", []sql.Row{{types.NewOkResult(0)}}, nil, nil)

// truncating time to microseconds for compatibility with integrators who may store more precision (go gives nanos)
now := time.Now().Truncate(time.Microsecond).UTC()
Expand Down Expand Up @@ -5824,14 +5824,14 @@ func TestColumnDefaults(t *testing.T, harness Harness) {
})

t.Run("Column defaults with functions", func(t *testing.T) {
TestQueryWithContext(t, ctx, e, harness, "CREATE TABLE t33(pk varchar(100) DEFAULT (replace(UUID(), '-', '')), v1 timestamp DEFAULT now(), v2 varchar(100), primary key (pk))", []sql.Row{{types.NewOkResult(0)}}, nil, nil)
TestQueryWithContext(t, ctx, e, harness, "CREATE TABLE t33(pk varchar(100) DEFAULT (replace(UUID(), '-', '')), v1 timestamp(6) DEFAULT now(), v2 varchar(100), primary key (pk))", []sql.Row{{types.NewOkResult(0)}}, nil, nil)
TestQueryWithContext(t, ctx, e, harness, "insert into t33 (v2) values ('abc')", []sql.Row{{types.NewOkResult(1)}}, nil, nil)
TestQueryWithContext(t, ctx, e, harness, "select count(*) from t33", []sql.Row{{1}}, nil, nil)
RunQuery(t, e, harness, "alter table t33 add column name varchar(100)")
RunQuery(t, e, harness, "alter table t33 rename column v1 to v1_new")
RunQuery(t, e, harness, "alter table t33 rename column name to name2")
RunQuery(t, e, harness, "alter table t33 drop column name2")
RunQuery(t, e, harness, "alter table t33 add column v3 datetime default CURRENT_TIMESTAMP()")
RunQuery(t, e, harness, "alter table t33 add column v3 datetime(6) default CURRENT_TIMESTAMP()")

TestQueryWithContext(t, ctx, e, harness, "desc t33", []sql.Row{
{"pk", "varchar(100)", "NO", "PRI", "(replace(uuid(), '-', ''))", "DEFAULT_GENERATED"},
Expand Down
75 changes: 73 additions & 2 deletions enginetest/memory_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"log"
"testing"
"time"

"github.com/dolthub/go-mysql-server/enginetest"
"github.com/dolthub/go-mysql-server/enginetest/queries"
Expand Down Expand Up @@ -276,10 +277,80 @@ func TestSingleQueryPrepared(t *testing.T) {
// Convenience test for debugging a single query. Unskip and set to the desired query.
func TestSingleScript(t *testing.T) {
t.Skip()
var scripts = []queries.ScriptTest{}
var scripts = []queries.ScriptTest{
{
Name: "datetime precision",
SetUpScript: []string{
"CREATE TABLE t1 (pk int primary key, d datetime)",
"CREATE TABLE t2 (pk int primary key, d datetime(3))",
"CREATE TABLE t3 (pk int primary key, d datetime(6))",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "show create table t1",
Expected: []sql.Row{{"t1",
"CREATE TABLE `t1` (\n" +
" `pk` int NOT NULL,\n" +
" `d` datetime(0),\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t1 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t1 order by pk",
Expected: []sql.Row{{1, queries.MustParseTime(time.DateTime, "2020-01-01 00:00:00")}},
},
{
Query: "show create table t2",
Expected: []sql.Row{{"t2",
"CREATE TABLE `t2` (\n" +
" `pk` int NOT NULL,\n" +
" `d` datetime(3),\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t2 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t2 order by pk",
Expected: []sql.Row{{1, queries.MustParseTime(time.RFC3339Nano, "2020-01-01T00:00:00.123000000Z")}},
},
{
Query: "show create table t3",
Expected: []sql.Row{{"t3",
"CREATE TABLE `t3` (\n" +
" `pk` int NOT NULL,\n" +
" `d` datetime(6),\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t3 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t3 order by pk",
Expected: []sql.Row{{1, queries.MustParseTime(time.RFC3339Nano, "2020-01-01T00:00:00.123456000Z")}},
},
{
Query: "create table t4 (pk int primary key, d datetime(-1))",
ExpectedErr: sql.ErrSyntaxError,
},
{
Query: "create table t4 (pk int primary key, d datetime(7))",
ExpectedErrStr: "DATETIME supports precision from 0 to 6",
},
},
},
}

for _, test := range scripts {
harness := enginetest.NewMemoryHarness("", 1, testNumPartitions, true, nil).WithVersion(sql.VersionExperimental)
harness := enginetest.NewMemoryHarness("", 1, testNumPartitions, true, nil)
engine, err := harness.NewEngine(t)
if err != nil {
panic(err)
Expand Down
2 changes: 1 addition & 1 deletion enginetest/queries/alter_table_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var AlterTableScripts = []ScriptTest{
// https://github.com/dolthub/dolt/issues/6206
Name: "alter table containing column default value expressions",
SetUpScript: []string{
"create table t (pk int primary key, col1 timestamp default current_timestamp(), col2 varchar(1000), index idx1 (pk, col1));",
"create table t (pk int primary key, col1 timestamp(6) default current_timestamp(), col2 varchar(1000), index idx1 (pk, col1));",
},
Assertions: []ScriptTestAssertion{
{
Expand Down
144 changes: 142 additions & 2 deletions enginetest/queries/create_table_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package queries

import (
"time"

"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/types"
)
Expand All @@ -30,7 +32,7 @@ var CreateTableQueries = []WriteQueryTest{
WriteQuery: `CREATE TABLE t1 (a INTEGER, b TEXT, c DATE, d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL, b1 BOOL, b2 BOOLEAN NOT NULL, g DATETIME, h CHAR(40))`,
ExpectedWriteResult: []sql.Row{{types.NewOkResult(0)}},
SelectQuery: "SHOW CREATE TABLE t1",
ExpectedSelect: []sql.Row{sql.Row{"t1", "CREATE TABLE `t1` (\n `a` int,\n `b` text,\n `c` date,\n `d` timestamp(6),\n `e` varchar(20),\n `f` blob NOT NULL,\n `b1` tinyint,\n `b2` tinyint NOT NULL,\n `g` datetime(6),\n `h` char(40)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
ExpectedSelect: []sql.Row{sql.Row{"t1", "CREATE TABLE `t1` (\n `a` int,\n `b` text,\n `c` date,\n `d` timestamp,\n `e` varchar(20),\n `f` blob NOT NULL,\n `b1` tinyint,\n `b2` tinyint NOT NULL,\n `g` datetime,\n `h` char(40)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
WriteQuery: `CREATE TABLE t1 (a INTEGER NOT NULL PRIMARY KEY, b VARCHAR(10) NOT NULL)`,
Expand Down Expand Up @@ -145,7 +147,7 @@ var CreateTableQueries = []WriteQueryTest{
)`,
ExpectedWriteResult: []sql.Row{{types.NewOkResult(0)}},
SelectQuery: "SHOW CREATE TABLE td",
ExpectedSelect: []sql.Row{sql.Row{"td", "CREATE TABLE `td` (\n `pk` int NOT NULL,\n `col2` int NOT NULL DEFAULT '2',\n `col3` double NOT NULL DEFAULT (round(-1.58,0)),\n `col4` varchar(10) DEFAULT 'new row',\n `col5` float DEFAULT '33.33',\n `col6` int DEFAULT NULL,\n `col7` timestamp(6) DEFAULT (NOW()),\n `col8` bigint DEFAULT (NOW()),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
ExpectedSelect: []sql.Row{sql.Row{"td", "CREATE TABLE `td` (\n `pk` int NOT NULL,\n `col2` int NOT NULL DEFAULT '2',\n `col3` double NOT NULL DEFAULT (round(-1.58,0)),\n `col4` varchar(10) DEFAULT 'new row',\n `col5` float DEFAULT '33.33',\n `col6` int DEFAULT NULL,\n `col7` timestamp DEFAULT (NOW()),\n `col8` bigint DEFAULT (NOW()),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
WriteQuery: `CREATE TABLE t1 (i int PRIMARY KEY, j varchar(MAX))`,
Expand Down Expand Up @@ -246,6 +248,144 @@ var CreateTableScriptTests = []ScriptTest{
},
},
},
{
Name: "datetime precision",
SetUpScript: []string{
"CREATE TABLE t1 (pk int primary key, d datetime)",
"CREATE TABLE t2 (pk int primary key, d datetime(3))",
"CREATE TABLE t3 (pk int primary key, d datetime(6))",
},
Assertions: []ScriptTestAssertion{
{
Query: "show create table t1",
Expected: []sql.Row{{"t1",
"CREATE TABLE `t1` (\n" +
" `pk` int NOT NULL,\n" +
" `d` datetime,\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t1 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t1 order by pk",
Expected: []sql.Row{{1, MustParseTime(time.DateTime, "2020-01-01 00:00:00")}},
},
{
Query: "show create table t2",
Expected: []sql.Row{{"t2",
"CREATE TABLE `t2` (\n" +
" `pk` int NOT NULL,\n" +
" `d` datetime(3),\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t2 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t2 order by pk",
Expected: []sql.Row{{1, MustParseTime(time.RFC3339Nano, "2020-01-01T00:00:00.123000000Z")}},
},
{
Query: "show create table t3",
Expected: []sql.Row{{"t3",
"CREATE TABLE `t3` (\n" +
" `pk` int NOT NULL,\n" +
" `d` datetime(6),\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t3 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t3 order by pk",
Expected: []sql.Row{{1, MustParseTime(time.RFC3339Nano, "2020-01-01T00:00:00.123456000Z")}},
},
{
Query: "create table t4 (pk int primary key, d datetime(-1))",
ExpectedErr: sql.ErrSyntaxError,
},
{
Query: "create table t4 (pk int primary key, d datetime(7))",
ExpectedErrStr: "DATETIME supports precision from 0 to 6",
},
},
},
{
Name: "timestamp precision",
SetUpScript: []string{
"CREATE TABLE t1 (pk int primary key, d timestamp)",
"CREATE TABLE t2 (pk int primary key, d timestamp(3))",
"CREATE TABLE t3 (pk int primary key, d timestamp(6))",
},
Assertions: []ScriptTestAssertion{
{
Query: "show create table t1",
Expected: []sql.Row{{"t1",
"CREATE TABLE `t1` (\n" +
" `pk` int NOT NULL,\n" +
" `d` timestamp,\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t1 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t1 order by pk",
Expected: []sql.Row{{1, MustParseTime(time.DateTime, "2020-01-01 00:00:00")}},
},
{
Query: "show create table t2",
Expected: []sql.Row{{"t2",
"CREATE TABLE `t2` (\n" +
" `pk` int NOT NULL,\n" +
" `d` timestamp(3),\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t2 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t2 order by pk",
Expected: []sql.Row{{1, MustParseTime(time.RFC3339Nano, "2020-01-01T00:00:00.123000000Z")}},
},
{
Query: "show create table t3",
Expected: []sql.Row{{"t3",
"CREATE TABLE `t3` (\n" +
" `pk` int NOT NULL,\n" +
" `d` timestamp(6),\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
{
Query: "insert into t3 values (1, '2020-01-01 00:00:00.123456')",
Expected: []sql.Row{{types.NewOkResult(1)}},
},
{
Query: "select * from t3 order by pk",
Expected: []sql.Row{{1, MustParseTime(time.RFC3339Nano, "2020-01-01T00:00:00.123456000Z")}},
},
{
Query: "create table t4 (pk int primary key, d TIMESTAMP(-1))",
ExpectedErr: sql.ErrSyntaxError,
},
{
Query: "create table t4 (pk int primary key, d TIMESTAMP(7))",
ExpectedErrStr: "TIMESTAMP supports precision from 0 to 6",
},
},
},
}

var CreateTableAutoIncrementTests = []ScriptTest{
Expand Down
8 changes: 4 additions & 4 deletions enginetest/queries/information_schema_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -972,8 +972,8 @@ FROM INFORMATION_SCHEMA.TRIGGERS WHERE trigger_schema = 'mydb'`,
{"about", "id", nil, "NO", "int unsigned", "UNI", nil, "auto_increment"},
{"about", "uuid", nil, "NO", "char(36)", "PRI", 36, ""},
{"about", "status", "draft", "NO", "varchar(255)", "", 255, ""},
{"about", "date_created", nil, "YES", "timestamp(6)", "", nil, ""},
{"about", "date_updated", nil, "YES", "timestamp(6)", "", nil, ""},
{"about", "date_created", nil, "YES", "timestamp", "", nil, ""},
{"about", "date_updated", nil, "YES", "timestamp", "", nil, ""},
{"about", "url_key", nil, "NO", "varchar(255)", "UNI", 255, ""},
},
},
Expand Down Expand Up @@ -1095,7 +1095,7 @@ bit_2 bit(2) DEFAULT 2,
some_blob blob DEFAULT ("abc"),
char_1 char(1) DEFAULT "A",
some_date date DEFAULT "2022-02-22",
date_time datetime DEFAULT "2022-02-22 22:22:21",
date_time datetime(6) DEFAULT "2022-02-22 22:22:21",
decimal_52 decimal(5,2) DEFAULT "994.45",
some_double double DEFAULT "1.1",
some_enum enum('s','m','l') DEFAULT "s",
Expand All @@ -1115,7 +1115,7 @@ some_set set('one','two') DEFAULT "one,two",
small_int smallint DEFAULT "5",
some_text text DEFAULT ("abc"),
time_6 time(6) DEFAULT "11:59:59.000010",
time_stamp timestamp DEFAULT (CURRENT_TIMESTAMP()),
time_stamp timestamp(6) DEFAULT (CURRENT_TIMESTAMP()),
tiny_blob tinyblob DEFAULT ("abc"),
tiny_int tinyint DEFAULT "4",
tiny_text tinytext DEFAULT ("abc"),
Expand Down
8 changes: 8 additions & 0 deletions enginetest/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -10376,3 +10376,11 @@ var IndexPrefixQueries = []ScriptTest{
},
},
}

func MustParseTime(layout, value string) time.Time {
parsed, err := time.Parse(layout, value)
if err != nil {
panic(err)
}
return parsed
}
2 changes: 1 addition & 1 deletion enginetest/queries/script_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -1859,7 +1859,7 @@ var ScriptTests = []ScriptTest{
"create table t2(c int primary key, d varchar(10))",
"alter table t2 add constraint t2du unique (d)",
"alter table t2 add constraint fk1 foreign key (d) references t1 (b)",
"create table t3 (a int, b varchar(100), c datetime, primary key (b,a))",
"create table t3 (a int, b varchar(100), c datetime(6), primary key (b,a))",
"create table t4 (a int default (floor(1)), b int default (coalesce(a, 10)))",
},
Assertions: []ScriptTestAssertion{
Expand Down
Loading