diff --git a/.github/workflows/cluster_endtoend_mysql80.yml b/.github/workflows/cluster_endtoend_mysql80.yml new file mode 100644 index 00000000000..4f075015c7e --- /dev/null +++ b/.github/workflows/cluster_endtoend_mysql80.yml @@ -0,0 +1,39 @@ +# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" + +name: Cluster (mysql80) +on: [push, pull_request] +jobs: + + build: + runs-on: ubuntu-20.04 + + steps: + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.15 + + - name: Check out code + uses: actions/checkout@v2 + + - name: Get dependencies + run: | + sudo apt-get update + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo service mysql stop + sudo service etcd stop + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld + go mod download + + wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb + sudo apt-get install -y gnupg2 + sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb + sudo apt-get update + sudo apt-get install percona-xtrabackup-24 + + - name: Run cluster endtoend test + timeout-minutes: 30 + run: | + source build.env + eatmydata -- go run test.go -docker=false -print-log -follow -shard mysql80 diff --git a/go/test/endtoend/vtgate/misc_test.go b/go/test/endtoend/vtgate/misc_test.go index 09a075556ae..fabcc5941cf 100644 --- a/go/test/endtoend/vtgate/misc_test.go +++ b/go/test/endtoend/vtgate/misc_test.go @@ -510,6 +510,20 @@ func TestSubQueryOnTopOfSubQuery(t *testing.T) { assertMatches(t, conn, "select id1 from t1 where id1 not in (select id3 from t2) and id2 in (select id4 from t2)", `[[INT64(3)] [INT64(4)]]`) } +func TestFunctionInDefault(t *testing.T) { + defer cluster.PanicHandler(t) + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + _, err = conn.ExecuteFetch(`create table function_default (x varchar(25) DEFAULT (TRIM(" check ")))`, 1000, true) + // this query fails because mysql57 does not support functions in default clause + require.Error(t, err) + exec(t, conn, `create table function_default (x varchar(25) DEFAULT "check")`) + exec(t, conn, "drop table function_default") +} + func assertMatches(t *testing.T, conn *mysql.Conn, query, expected string) { t.Helper() qr := exec(t, conn, query) diff --git a/go/test/endtoend/vtgate/mysql80/main_test.go b/go/test/endtoend/vtgate/mysql80/main_test.go new file mode 100644 index 00000000000..56661010449 --- /dev/null +++ b/go/test/endtoend/vtgate/mysql80/main_test.go @@ -0,0 +1,70 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtgate + +import ( + "flag" + "os" + "testing" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + vtParams mysql.ConnParams + KeyspaceName = "ks" + Cell = "test" +) + +func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) + flag.Parse() + + exitCode := func() int { + clusterInstance = cluster.NewCluster(Cell, "localhost") + defer clusterInstance.Teardown() + + // Start topo server + err := clusterInstance.StartTopo() + if err != nil { + return 1 + } + + // Start keyspace + keyspace := &cluster.Keyspace{ + Name: KeyspaceName, + } + err = clusterInstance.StartUnshardedKeyspace(*keyspace, 0, false) + if err != nil { + return 1 + } + + // Start vtgate + err = clusterInstance.StartVtgate() + if err != nil { + return 1 + } + vtParams = mysql.ConnParams{ + Host: clusterInstance.Hostname, + Port: clusterInstance.VtgateMySQLPort, + } + return m.Run() + }() + os.Exit(exitCode) +} diff --git a/go/test/endtoend/vtgate/mysql80/misc_test.go b/go/test/endtoend/vtgate/mysql80/misc_test.go new file mode 100644 index 00000000000..72e4eff8c69 --- /dev/null +++ b/go/test/endtoend/vtgate/mysql80/misc_test.go @@ -0,0 +1,50 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtgate + +import ( + "context" + "testing" + + "vitess.io/vitess/go/test/endtoend/cluster" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" +) + +func TestFunctionInDefault(t *testing.T) { + defer cluster.PanicHandler(t) + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + exec(t, conn, `create table function_default (x varchar(25) DEFAULT (TRIM(" check ")))`) + exec(t, conn, "drop table function_default") + + exec(t, conn, `create table function_default (x varchar(25) DEFAULT "check")`) + exec(t, conn, "drop table function_default") +} + +func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { + t.Helper() + qr, err := conn.ExecuteFetch(query, 1000, true) + require.NoError(t, err, "for query: "+query) + return qr +} diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index c508c526c8e..310799db515 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -2220,7 +2220,14 @@ func (ct *ColumnType) Format(buf *TrackedBuffer) { opts = append(opts, keywordStrings[NOT], keywordStrings[NULL]) } if ct.Default != nil { - opts = append(opts, keywordStrings[DEFAULT], String(ct.Default)) + opts = append(opts, keywordStrings[DEFAULT]) + _, isLiteral := ct.Default.(*Literal) + _, isNullVal := ct.Default.(*NullVal) + if isLiteral || isNullVal { + opts = append(opts, String(ct.Default)) + } else { + opts = append(opts, "("+String(ct.Default)+")") + } } if ct.OnUpdate != nil { opts = append(opts, keywordStrings[ON], keywordStrings[UPDATE], String(ct.OnUpdate)) diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index bda582166d7..94298152e1b 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1078,6 +1078,15 @@ var ( output: "alter database d collate 'utf8_bin' character set geostd8 character set geostd8", }, { input: "create table a", + }, { + input: "create table function_default (x varchar(25) default (trim(' check ')))", + output: "create table function_default (\n\tx varchar(25) default (trim(' check '))\n)", + }, { + input: "create table function_default (x varchar(25) default (((trim(' check ')))))", + output: "create table function_default (\n\tx varchar(25) default (trim(' check '))\n)", + }, { + input: "create table function_default3 (x bool DEFAULT (true AND false));", + output: "create table function_default3 (\n\tx bool default (true and false)\n)", }, { input: "create table a (\n\t`a` int\n)", output: "create table a (\n\ta int\n)", @@ -2630,7 +2639,7 @@ func TestCreateTable(t *testing.T) { " s1 varchar default 'c',\n" + " s2 varchar default 'this is a string',\n" + " `s3` varchar default null,\n" + - " s4 timestamp default current_timestamp(),\n" + + " s4 timestamp default (current_timestamp()),\n" + " s5 bit(1) default B'0'\n" + ")", }, { @@ -2657,7 +2666,7 @@ func TestCreateTable(t *testing.T) { " email varchar unique,\n" + " full_name varchar key,\n" + " time1 timestamp on update current_timestamp(),\n" + - " time2 timestamp default current_timestamp() on update current_timestamp()\n" + + " time2 timestamp default (current_timestamp()) on update current_timestamp()\n" + ")", }, { // test current_timestamp with and without () @@ -2669,11 +2678,11 @@ func TestCreateTable(t *testing.T) { " time5 timestamp(3) default current_timestamp(3) on update current_timestamp(3)\n" + ")", output: "create table t (\n" + - " time1 timestamp default current_timestamp(),\n" + - " time2 timestamp default current_timestamp(),\n" + - " time3 timestamp default current_timestamp() on update current_timestamp(),\n" + - " time4 timestamp default current_timestamp() on update current_timestamp(),\n" + - " time5 timestamp(3) default current_timestamp(3) on update current_timestamp(3)\n" + + " time1 timestamp default (current_timestamp()),\n" + + " time2 timestamp default (current_timestamp()),\n" + + " time3 timestamp default (current_timestamp()) on update current_timestamp(),\n" + + " time4 timestamp default (current_timestamp()) on update current_timestamp(),\n" + + " time5 timestamp(3) default (current_timestamp(3)) on update current_timestamp(3)\n" + ")", }, { // test utc_timestamp with and without () @@ -2685,11 +2694,11 @@ func TestCreateTable(t *testing.T) { " time5 timestamp(4) default utc_timestamp(4) on update utc_timestamp(4)\n" + ")", output: "create table t (\n" + - " time1 timestamp default utc_timestamp(),\n" + - " time2 timestamp default utc_timestamp(),\n" + - " time3 timestamp default utc_timestamp() on update utc_timestamp(),\n" + - " time4 timestamp default utc_timestamp() on update utc_timestamp(),\n" + - " time5 timestamp(4) default utc_timestamp(4) on update utc_timestamp(4)\n" + + " time1 timestamp default (utc_timestamp()),\n" + + " time2 timestamp default (utc_timestamp()),\n" + + " time3 timestamp default (utc_timestamp()) on update utc_timestamp(),\n" + + " time4 timestamp default (utc_timestamp()) on update utc_timestamp(),\n" + + " time5 timestamp(4) default (utc_timestamp(4)) on update utc_timestamp(4)\n" + ")", }, { // test utc_time with and without () @@ -2701,11 +2710,11 @@ func TestCreateTable(t *testing.T) { " time5 timestamp(5) default utc_time(5) on update utc_time(5)\n" + ")", output: "create table t (\n" + - " time1 timestamp default utc_time(),\n" + - " time2 timestamp default utc_time(),\n" + - " time3 timestamp default utc_time() on update utc_time(),\n" + - " time4 timestamp default utc_time() on update utc_time(),\n" + - " time5 timestamp(5) default utc_time(5) on update utc_time(5)\n" + + " time1 timestamp default (utc_time()),\n" + + " time2 timestamp default (utc_time()),\n" + + " time3 timestamp default (utc_time()) on update utc_time(),\n" + + " time4 timestamp default (utc_time()) on update utc_time(),\n" + + " time5 timestamp(5) default (utc_time(5)) on update utc_time(5)\n" + ")", }, { // test utc_date with and without () @@ -2716,10 +2725,10 @@ func TestCreateTable(t *testing.T) { " time4 timestamp default utc_date() on update utc_date()\n" + ")", output: "create table t (\n" + - " time1 timestamp default utc_date(),\n" + - " time2 timestamp default utc_date(),\n" + - " time3 timestamp default utc_date() on update utc_date(),\n" + - " time4 timestamp default utc_date() on update utc_date()\n" + + " time1 timestamp default (utc_date()),\n" + + " time2 timestamp default (utc_date()),\n" + + " time3 timestamp default (utc_date()) on update utc_date(),\n" + + " time4 timestamp default (utc_date()) on update utc_date()\n" + ")", }, { // test localtime with and without () @@ -2731,11 +2740,11 @@ func TestCreateTable(t *testing.T) { " time5 timestamp(6) default localtime(6) on update localtime(6)\n" + ")", output: "create table t (\n" + - " time1 timestamp default localtime(),\n" + - " time2 timestamp default localtime(),\n" + - " time3 timestamp default localtime() on update localtime(),\n" + - " time4 timestamp default localtime() on update localtime(),\n" + - " time5 timestamp(6) default localtime(6) on update localtime(6)\n" + + " time1 timestamp default (localtime()),\n" + + " time2 timestamp default (localtime()),\n" + + " time3 timestamp default (localtime()) on update localtime(),\n" + + " time4 timestamp default (localtime()) on update localtime(),\n" + + " time5 timestamp(6) default (localtime(6)) on update localtime(6)\n" + ")", }, { // test localtimestamp with and without () @@ -2747,11 +2756,11 @@ func TestCreateTable(t *testing.T) { " time5 timestamp(1) default localtimestamp(1) on update localtimestamp(1)\n" + ")", output: "create table t (\n" + - " time1 timestamp default localtimestamp(),\n" + - " time2 timestamp default localtimestamp(),\n" + - " time3 timestamp default localtimestamp() on update localtimestamp(),\n" + - " time4 timestamp default localtimestamp() on update localtimestamp(),\n" + - " time5 timestamp(1) default localtimestamp(1) on update localtimestamp(1)\n" + + " time1 timestamp default (localtimestamp()),\n" + + " time2 timestamp default (localtimestamp()),\n" + + " time3 timestamp default (localtimestamp()) on update localtimestamp(),\n" + + " time4 timestamp default (localtimestamp()) on update localtimestamp(),\n" + + " time5 timestamp(1) default (localtimestamp(1)) on update localtimestamp(1)\n" + ")", }, { // test current_date with and without () @@ -2762,10 +2771,10 @@ func TestCreateTable(t *testing.T) { " time4 timestamp default current_date() on update current_date()\n" + ")", output: "create table t (\n" + - " time1 timestamp default current_date(),\n" + - " time2 timestamp default current_date(),\n" + - " time3 timestamp default current_date() on update current_date(),\n" + - " time4 timestamp default current_date() on update current_date()\n" + + " time1 timestamp default (current_date()),\n" + + " time2 timestamp default (current_date()),\n" + + " time3 timestamp default (current_date()) on update current_date(),\n" + + " time4 timestamp default (current_date()) on update current_date()\n" + ")", }, { // test current_time with and without () @@ -2777,11 +2786,11 @@ func TestCreateTable(t *testing.T) { " time5 timestamp(2) default current_time(2) on update current_time(2)\n" + ")", output: "create table t (\n" + - " time1 timestamp default current_time(),\n" + - " time2 timestamp default current_time(),\n" + - " time3 timestamp default current_time() on update current_time(),\n" + - " time4 timestamp default current_time() on update current_time(),\n" + - " time5 timestamp(2) default current_time(2) on update current_time(2)\n" + + " time1 timestamp default (current_time()),\n" + + " time2 timestamp default (current_time()),\n" + + " time3 timestamp default (current_time()) on update current_time(),\n" + + " time4 timestamp default (current_time()) on update current_time(),\n" + + " time5 timestamp(2) default (current_time(2)) on update current_time(2)\n" + ")", }, } diff --git a/go/vt/vtgate/planbuilder/testdata/ddl_cases.txt b/go/vt/vtgate/planbuilder/testdata/ddl_cases.txt index c1c1fb980ab..4af27601104 100644 --- a/go/vt/vtgate/planbuilder/testdata/ddl_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/ddl_cases.txt @@ -293,3 +293,18 @@ "Query": "drop view a" } } + +# create table with function as a default value +"create table function_default (x varchar(25) DEFAULT (TRIM(' check ')))" +{ + "QueryType": "DDL", + "Original": "create table function_default (x varchar(25) DEFAULT (TRIM(' check ')))", + "Instructions": { + "OperatorType": "DDL", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "create table function_default (\n\tx varchar(25) default (TRIM(' check '))\n)" + } +} diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index a69b3562f6f..a3990256fcb 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -903,3 +903,17 @@ options:PassthroughDMLs ], "FullQuery":"drop view a, b" } + +# create table with function as a default value +"create table function_default (x varchar(25) DEFAULT (TRIM(' check ')))" +{ + "PlanID": "DDL", + "TableName": "", + "Permissions": [ + { + "TableName": "function_default", + "Role": 2 + } + ], + "FullQuery": "create table function_default (\n\tx varchar(25) default (TRIM(' check '))\n)" +} diff --git a/test/ci_workflow_gen.go b/test/ci_workflow_gen.go index 2f4bbb126ff..a1ff08cc799 100644 --- a/test/ci_workflow_gen.go +++ b/test/ci_workflow_gen.go @@ -32,11 +32,12 @@ const ( unitTestDatabases = "percona56, mysql57, mysql80, mariadb101, mariadb102, mariadb103" clusterTestTemplate = "templates/cluster_endtoend_test.tpl" - clusterList = "11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,vreplication_basic,vreplication_multicell,vreplication_cellalias,vreplication_v2" + clusterList = "11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,vreplication_basic,vreplication_multicell,vreplication_cellalias,vreplication_v2,mysql80" // TODO: currently some percona tools including xtrabackup are installed on all clusters, we can possibly optimize // this by only installing them in the required clusters clustersRequiringXtraBackup = clusterList clustersRequiringMakeTools = "18,24" + clustersRequiringUbuntu20 = "mysql80" ) type unitTest struct { @@ -46,6 +47,7 @@ type unitTest struct { type clusterTest struct { Name, Shard string MakeTools, InstallXtraBackup bool + Ubuntu20 bool } func mergeBlankLines(buf *bytes.Buffer) string { @@ -101,6 +103,13 @@ func generateClusterWorkflows() { break } } + ubuntu20Clusters := parseList(clustersRequiringUbuntu20) + for _, ubuntu20Cluster := range ubuntu20Clusters { + if ubuntu20Cluster == cluster { + test.Ubuntu20 = true + break + } + } path := fmt.Sprintf("%s/cluster_endtoend_%s.yml", workflowConfigDir, cluster) generateWorkflowFile(clusterTestTemplate, path, test) diff --git a/test/config.json b/test/config.json index a535d3178a5..ff3b040715c 100644 --- a/test/config.json +++ b/test/config.json @@ -497,6 +497,15 @@ "RetryMax": 0, "Tags": [] }, + "vtgate_mysql80": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/mysql80"], + "Command": [], + "Manual": false, + "Shard": "mysql80", + "RetryMax": 0, + "Tags": [] + }, "vtgate_sequence": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/sequence"], diff --git a/test/templates/cluster_endtoend_test.tpl b/test/templates/cluster_endtoend_test.tpl index 60e4fcce311..02fb5e9e305 100644 --- a/test/templates/cluster_endtoend_test.tpl +++ b/test/templates/cluster_endtoend_test.tpl @@ -3,7 +3,7 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-18.04 + {{if .Ubuntu20}}runs-on: ubuntu-20.04{{else}}runs-on: ubuntu-18.04{{end}} steps: - name: Set up Go