Skip to content

Commit 4802499

Browse files
authored
Change collation lookup to be durable across additional engine/versions (petoju#158)
* Change collation lookup to be durable across additional engine/versions This patch looks up collation based on INFORMATION_SCHEMA.COLLATIONS and scopes the response to just the two required columns. With this patch it's: 1. Mysql 5.7 and 8.0 without special casing 2. MariaDB without special casing 3. compatible with TiDB for 6.x and 7.x (v7.5.1 which advertises as 8.0.11-TiDB-v7.5.1 but lacks the 7th column in SHOW statement previously used) * Enable tidb testing for resource_database * Fix tests for TiDB and add TiDB 7.5 * Update README about local test running * Enable support for TiDB 7.5.2 and disable selective tests * Update mysql/provider.go * Remove debugging code * Refactor to simplify testAccs that overlap * Add back in test skips where it fails for TiDB 7.5.2 These are skipped because of syntactical differences between mysql and TiDB. These tend to have mariadb/rds/tidb matching more closely in behavior.
1 parent f0d041e commit 4802499

11 files changed

+94
-55
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@ workflows:
3838
- integration:
3939
matrix:
4040
parameters:
41-
target: ["testversion5.6", "testversion5.7", "testversion8.0", "testpercona5.7", "testpercona8.0", "testmariadb10.3", "testmariadb10.8", "testmariadb10.10", "testtidb6.1.0"]
41+
target: ["testversion5.6", "testversion5.7", "testversion8.0", "testpercona5.7", "testpercona8.0", "testmariadb10.3", "testmariadb10.8", "testmariadb10.10", "testtidb6.1.0", "testtidb7.5.2"]
4242
- govet

GNUmakefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ bin/terraform:
2222
testacc: fmtcheck bin/terraform
2323
PATH="$(CURDIR)/bin:${PATH}" TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout=90s
2424

25-
acceptance: testversion5.6 testversion5.7 testversion8.0 testpercona5.7 testpercona8.0 testmariadb10.3 testmariadb10.8 testmariadb10.10 testtidb6.1.0
25+
acceptance: testversion5.6 testversion5.7 testversion8.0 testpercona5.7 testpercona8.0 testmariadb10.3 testmariadb10.8 testmariadb10.10 testtidb6.1.0 testtidb7.5.2
2626

2727
testversion%:
2828
$(MAKE) MYSQL_VERSION=$* MYSQL_PORT=33$(shell echo "$*" | tr -d '.') testversion
@@ -57,6 +57,8 @@ testrdsdb:
5757
testtidb%:
5858
$(MAKE) MYSQL_VERSION=$* MYSQL_PORT=34$(shell echo "$*" | tr -d '.') testtidb
5959

60+
# WARNING: this does not work as a bare task run, it only instantiates correctly inside the versioned TiDB task run
61+
# otherwise MYSQL_PORT and version are unset.
6062
testtidb:
6163
@sh -c "'$(CURDIR)/scripts/tidb-test-cluster.sh' --init --port $(MYSQL_PORT) --version $(MYSQL_VERSION)"
6264
@echo 'Waiting for TiDB...'

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ $ make bin
6565
$ $GOPATH/bin/terraform-provider-mysql
6666
...
6767
```
68+
### Ensure local requirements are present:
69+
70+
1. Docker environment
71+
2. mysql-client binary which can be installed on Mac with `brew install [email protected]`
72+
1. Then add it to your path OR `brew link [email protected]`
73+
74+
### Running tests
6875

6976
In order to test the provider, you can simply run `make test`.
7077

mysql/provider.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,25 @@ func serverVersionString(db *sql.DB) (string, error) {
542542
return versionString, nil
543543
}
544544

545+
// serverTiDB returns:
546+
// - it is a TiDB instance
547+
// - tidbVersion
548+
// - mysqlCompatibilityVersion
549+
// - err
550+
func serverTiDB(db *sql.DB) (bool, string, string, error) {
551+
currentVersionString, err := serverVersionString(db)
552+
if err != nil {
553+
return false, "", "", err
554+
}
555+
556+
if strings.Contains(currentVersionString, "TiDB") {
557+
versions := strings.SplitN(currentVersionString, "-", 3)
558+
return true, versions[2], versions[0], nil
559+
}
560+
561+
return false, "", "", nil
562+
}
563+
545564
func serverRds(db *sql.DB) (bool, error) {
546565
var metadataVersionString string
547566
err := db.QueryRow("SELECT @@GLOBAL.datadir").Scan(&metadataVersionString)

mysql/provider_test.go

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package mysql
33
import (
44
"context"
55
"fmt"
6-
"github.com/hashicorp/go-version"
76
"os"
87
"strings"
98
"testing"
109

10+
"github.com/hashicorp/go-version"
11+
1112
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1213
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
1314
)
@@ -153,25 +154,7 @@ func testAccPreCheckSkipMariaDB(t *testing.T) {
153154
}
154155

155156
func testAccPreCheckSkipNotMySQL8(t *testing.T) {
156-
testAccPreCheck(t)
157-
158-
ctx := context.Background()
159-
db, err := connectToMySQL(ctx, testAccProvider.Meta().(*MySQLConfiguration))
160-
if err != nil {
161-
t.Fatalf("Cannot connect to DB (SkipNotMySQL8): %v", err)
162-
return
163-
}
164-
165-
currentVersion, err := serverVersion(db)
166-
if err != nil {
167-
t.Fatalf("Cannot get DB version string (SkipNotMySQL8): %v", err)
168-
return
169-
}
170-
171-
versionMin, _ := version.NewVersion("8.0.0")
172-
if currentVersion.LessThan(versionMin) {
173-
t.Skip("Skip on MySQL8")
174-
}
157+
testAccPreCheckSkipNotMySQLVersionMin(t, "8.0.0")
175158
}
176159

177160
func testAccPreCheckSkipNotMySQLVersionMin(t *testing.T, minVersion string) {
@@ -180,19 +163,36 @@ func testAccPreCheckSkipNotMySQLVersionMin(t *testing.T, minVersion string) {
180163
ctx := context.Background()
181164
db, err := connectToMySQL(ctx, testAccProvider.Meta().(*MySQLConfiguration))
182165
if err != nil {
183-
t.Fatalf("Cannot connect to DB (SkipNotMySQLVersionMin): %v", err)
166+
t.Fatalf("Cannot connect to DB (SkipNotMySQL8): %v", err)
184167
return
185168
}
186169

187170
currentVersion, err := serverVersion(db)
188171
if err != nil {
189-
t.Fatalf("Cannot get DB version string (SkipNotMySQLVersionMin): %v", err)
172+
t.Fatalf("Cannot get DB version string (SkipNotMySQL8): %v", err)
190173
return
191174
}
192175

193176
versionMin, _ := version.NewVersion(minVersion)
194177
if currentVersion.LessThan(versionMin) {
195-
t.Skipf("Skip on MySQL version less than %s", minVersion)
178+
// TiDB 7.x series advertises as 8.0 mysql so we batch its testing strategy with Mysql8
179+
isTiDB, tidbVersion, mysqlCompatibilityVersion, err := serverTiDB(db)
180+
if err != nil {
181+
t.Fatalf("Cannot get DB version string (SkipNotMySQL8): %v", err)
182+
return
183+
}
184+
if isTiDB {
185+
mysqlVersion, err := version.NewVersion(mysqlCompatibilityVersion)
186+
if err != nil {
187+
t.Fatalf("Cannot get DB version string for TiDB (SkipNotMySQL8): %s %s %v", tidbVersion, mysqlCompatibilityVersion, err)
188+
return
189+
}
190+
if mysqlVersion.LessThan(versionMin) {
191+
t.Skip("Skip on MySQL8")
192+
}
193+
}
194+
195+
t.Skip("Skip on MySQL8")
196196
}
197197
}
198198

mysql/resource_database.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import (
55
"database/sql"
66
"errors"
77
"fmt"
8-
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
98
"log"
109
"strings"
1110

12-
"github.com/hashicorp/go-version"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12+
1313
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1414
)
1515

@@ -116,23 +116,22 @@ func ReadDatabase(ctx context.Context, d *schema.ResourceData, meta interface{})
116116
// MySQL doesn't return the collation if it's the default one for
117117
// the charset, so if we don't have a collation we need to go
118118
// hunt for the default.
119-
stmtSQL := "SHOW COLLATION WHERE `Charset` = ? AND `Default` = 'Yes'"
120-
var empty interface{}
119+
stmtSQL := "SELECT COLLATION_NAME, CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE CHARACTER_SET_NAME = ? AND `IS_DEFAULT` = 'Yes';"
120+
/*
121+
Mysql (5.7, 8.0), TiDB (6.x, 7.x) example:
122+
> SELECT COLLATION_NAME, CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE CHARACTER_SET_NAME = 'utf8mb4' AND `IS_DEFAULT` = 'Yes';
121123
122-
requiredVersion, _ := version.NewVersion("8.0.0")
124+
+--------------------+--------------------+
125+
| COLLATION_NAME | CHARACTER_SET_NAME |
126+
+--------------------+--------------------+
127+
| utf8mb4_0900_ai_ci | utf8mb4 |
128+
+--------------------+--------------------+
123129
124-
serverVersionString, err := serverVersionString(db)
125-
if err != nil {
126-
return diag.Errorf("could not get error version string: %v", err)
127-
}
128130
129-
// MySQL 8 returns more data in a row.
130-
var res error
131-
if !strings.Contains(serverVersionString, "MariaDB") && getVersionFromMeta(ctx, meta).GreaterThan(requiredVersion) {
132-
res = db.QueryRow(stmtSQL, defaultCharset).Scan(&defaultCollation, &empty, &empty, &empty, &empty, &empty, &empty)
133-
} else {
134-
res = db.QueryRow(stmtSQL, defaultCharset).Scan(&defaultCollation, &empty, &empty, &empty, &empty, &empty)
135-
}
131+
*/
132+
var empty interface{}
133+
134+
res := db.QueryRow(stmtSQL, defaultCharset).Scan(&defaultCollation, &empty)
136135

137136
if res != nil {
138137
if errors.Is(res, sql.ErrNoRows) {

mysql/resource_database_test.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
func TestAccDatabase(t *testing.T) {
1414
dbName := "terraform_acceptance_test"
1515
resource.Test(t, resource.TestCase{
16-
PreCheck: func() { testAccPreCheckSkipTiDB(t) },
16+
PreCheck: func() {},
1717
ProviderFactories: testAccProviderFactories,
1818
CheckDestroy: testAccDatabaseCheckDestroy(dbName),
1919
Steps: []resource.TestStep{
@@ -46,7 +46,7 @@ func TestAccDatabase_collationChange(t *testing.T) {
4646
ctx := context.Background()
4747

4848
resource.Test(t, resource.TestCase{
49-
PreCheck: func() { testAccPreCheckSkipTiDB(t) },
49+
PreCheck: func() {},
5050
ProviderFactories: testAccProviderFactories,
5151
CheckDestroy: testAccDatabaseCheckDestroy(dbName),
5252
Steps: []resource.TestStep{
@@ -106,11 +106,21 @@ func testAccDatabaseCheckFull(rn string, name string, charset string, collation
106106
return fmt.Errorf("error reading database: %s", err)
107107
}
108108

109-
if strings.Index(createSQL, fmt.Sprintf("CHARACTER SET %s", charset)) == -1 {
109+
if !strings.Contains(createSQL, fmt.Sprintf("CHARACTER SET %s", charset)) {
110110
return fmt.Errorf("database default charset isn't %s", charset)
111111
}
112-
if strings.Index(createSQL, fmt.Sprintf("COLLATE %s", collation)) == -1 {
113-
return fmt.Errorf("database default collation isn't %s", collation)
112+
// TiDB does not include the COLLATE reference in `SHOW CREATE DATABASE`
113+
// so perform a lookup based on the charset to find default collation
114+
if !strings.Contains(createSQL, fmt.Sprintf("COLLATE %s", collation)) {
115+
sql := `SELECT COLLATION_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE IS_DEFAULT = 'Yes' AND CHARACTER_SET_NAME = ?;`
116+
var fetchedCollation string
117+
err = db.QueryRow(sql, charset).Scan(&fetchedCollation)
118+
if err != nil {
119+
return fmt.Errorf("database default collation expected %s vs actual %s with error: %e", collation, fetchedCollation, err)
120+
}
121+
if fetchedCollation != collation {
122+
return fmt.Errorf("database default collation expected %s vs actual %s", collation, fetchedCollation)
123+
}
114124
}
115125

116126
return nil

mysql/resource_default_roles_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func TestAccDefaultRoles_basic(t *testing.T) {
1616
testAccPreCheck(t)
1717
testAccPreCheckSkipNotMySQL8(t)
1818
testAccPreCheckSkipMariaDB(t)
19+
testAccPreCheckSkipTiDB(t)
1920
},
2021
ProviderFactories: testAccProviderFactories,
2122
CheckDestroy: testAccDefaultRolesCheckDestroy,

mysql/resource_grant_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,10 @@ func TestAccGrantComplexMySQL8(t *testing.T) {
297297
dbName := fmt.Sprintf("tf-test-%d", rand.Intn(100))
298298
resource.Test(t, resource.TestCase{
299299
PreCheck: func() {
300-
testAccPreCheckSkipTiDB(t)
301300
testAccPreCheckSkipRds(t)
302301
testAccPreCheckSkipMariaDB(t)
303302
testAccPreCheckSkipNotMySQLVersionMin(t, "8.0.0")
303+
testAccPreCheckSkipTiDB(t)
304304
},
305305
ProviderFactories: testAccProviderFactories,
306306
CheckDestroy: testAccGrantCheckDestroy,
@@ -369,6 +369,7 @@ func TestAccGrant_roleToUser(t *testing.T) {
369369
testAccPreCheck(t)
370370
testAccPreCheckSkipRds(t)
371371
testAccPreCheckSkipNotMySQLVersionMin(t, "8.0.0")
372+
testAccPreCheckSkipTiDB(t)
372373
},
373374
ProviderFactories: testAccProviderFactories,
374375
CheckDestroy: testAccGrantCheckDestroy,
@@ -392,6 +393,7 @@ func TestAccGrant_complexRoleGrants(t *testing.T) {
392393
testAccPreCheck(t)
393394
testAccPreCheckSkipMariaDB(t)
394395
testAccPreCheckSkipNotMySQLVersionMin(t, "8.0.0")
396+
testAccPreCheckSkipTiDB(t)
395397
},
396398
ProviderFactories: testAccProviderFactories,
397399
CheckDestroy: testAccGrantCheckDestroy,
@@ -1075,20 +1077,20 @@ func TestAllowDuplicateUsersDifferentTables(t *testing.T) {
10751077
resource "mysql_database" "test" {
10761078
name = "%s"
10771079
}
1078-
1080+
10791081
resource "mysql_user" "test" {
10801082
user = "jdoe-%s"
10811083
host = "example.com"
10821084
}
1083-
1085+
10841086
resource "mysql_grant" "grant1" {
10851087
user = "${mysql_user.test.user}"
10861088
host = "${mysql_user.test.host}"
10871089
database = "${mysql_database.test.name}"
10881090
table = "table1"
10891091
privileges = ["UPDATE", "SELECT"]
10901092
}
1091-
1093+
10921094
resource "mysql_grant" "grant2" {
10931095
user = "${mysql_user.test.user}"
10941096
host = "${mysql_user.test.host}"
@@ -1140,20 +1142,20 @@ func TestDisallowDuplicateUsersSameTable(t *testing.T) {
11401142
resource "mysql_database" "test" {
11411143
name = "%s"
11421144
}
1143-
1145+
11441146
resource "mysql_user" "test" {
11451147
user = "jdoe-%s"
11461148
host = "example.com"
11471149
}
1148-
1150+
11491151
resource "mysql_grant" "grant1" {
11501152
user = "${mysql_user.test.user}"
11511153
host = "${mysql_user.test.host}"
11521154
database = "${mysql_database.test.name}"
11531155
table = "table1"
11541156
privileges = ["UPDATE", "SELECT"]
11551157
}
1156-
1158+
11571159
resource "mysql_grant" "grant2" {
11581160
user = "${mysql_user.test.user}"
11591161
host = "${mysql_user.test.host}"

mysql/resource_user_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ func TestAccUser_authConnect(t *testing.T) {
133133
func TestAccUser_authConnectRetainOldPassword(t *testing.T) {
134134
resource.Test(t, resource.TestCase{
135135
PreCheck: func() {
136-
testAccPreCheckSkipTiDB(t)
137136
testAccPreCheckSkipMariaDB(t)
138137
testAccPreCheckSkipRds(t)
139138
testAccPreCheckSkipNotMySQLVersionMin(t, "8.0.14")

0 commit comments

Comments
 (0)