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

Integration test checkconstraints #44

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
32d4a47
added the integration test
Jan 3, 2025
ee9edeb
updated the integration test
Jan 3, 2025
5502a5b
updated the insert query
Jan 6, 2025
cf6cf12
removed unwanted code
Jan 6, 2025
e57b7cc
fixed minor bug related to dump flow
Jan 7, 2025
1a751d7
Replace ReadOnlyTransaction() with Single() (#984)
manitgupta Jan 7, 2025
fce03cc
Merge branch 'master' into integration_test_checkconstraints
akashthawaitcc Jan 7, 2025
6892b44
UI changes for check constraint support (#979)
taherkl Jan 7, 2025
1062bdf
Merge branch 'master' into integration_test_checkconstraints
akashthawaitcc Jan 8, 2025
bbf7417
resolve the IT issue
Jan 8, 2025
3340234
added missing cmd
Jan 8, 2025
cdb61bf
fixed the mysql query
Jan 8, 2025
ed753a1
fixed the query
Jan 8, 2025
a40eaf5
fixed the query (#45)
VivekY1098 Jan 9, 2025
293bbf7
comment addressed (#48)
VivekY1098 Jan 9, 2025
23148db
Documentation for check constraints (#980)
akashthawaitcc Jan 9, 2025
bbdd639
Merge branch 'master' into integration_test_checkconstraints
akashthawaitcc Jan 10, 2025
624457e
added fix (#49)
VivekY1098 Jan 10, 2025
793ee88
chore(deps): update dependency cypress to v13.17.0 (#982)
renovate-bot Jan 13, 2025
7abae1f
minor docs cleanup (#922)
bharadwaj-aditya Jan 13, 2025
bc21bb4
chore(deps): update dependency @types/node to v20.17.10 (#981)
renovate-bot Jan 13, 2025
db9afa4
Merge branch 'master' into integration_test_checkconstraints
VivekY1098 Jan 13, 2025
4fc3533
fixed the issue without check constraints (#50)
VivekY1098 Jan 13, 2025
aca3fb7
added the fix (#51)
VivekY1098 Jan 14, 2025
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: 2 additions & 0 deletions .github/workflows/integration-tests-against-emulator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
MYSQLUSER: root
MYSQLDATABASE: test_interleave_table_data
MYSQLDB_FKACTION: test_foreign_key_action_data
MYSQLDB_CHECK_CONSTRAINT: test_mysql_checkconstraint
MYSQLPWD: root

# set DynamoDB related environment variables
Expand Down Expand Up @@ -112,6 +113,7 @@ jobs:
- run: mysql -v -P 3306 --protocol=tcp -u root -proot test < test_data/mysqldump.test.out
- run: mysql -v -P 3306 --protocol=tcp -u root -proot < test_data/mysql_interleave_dump.test.out
- run: mysql -v -P 3306 --protocol=tcp -u root -proot < test_data/mysql_foreignkeyaction_dump.test.out
- run: mysql -v -P 3306 --protocol=tcp -u root -proot < test_data/mysql_checkconstraint_dump.test.out

# init sql server with test_data
# since we use ubuntu-latest container, we should ensure that the path matches the latest from https://packages.microsoft.com/config/ubuntu/
Expand Down
2 changes: 1 addition & 1 deletion accessors/clients/spanner/client/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (c *SpannerClientImpl) Refresh(ctx context.Context, dbURI string) error {
}

func (c *SpannerClientImpl) Single() ReadOnlyTransaction {
rotxn := c.spannerClient.ReadOnlyTransaction()
rotxn := c.spannerClient.Single()
return &ReadOnlyTransactionImpl{rotxn: rotxn}
}

Expand Down
2 changes: 2 additions & 0 deletions conversion/conversion_from_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ func (sads *SchemaFromSourceImpl) SchemaFromDump(SpProjectId string, SpInstanceI
conv := internal.MakeConv()
conv.SpDialect = spDialect
conv.Source = driver
conv.SpProjectId = SpProjectId
conv.SpInstanceId = SpInstanceId
p := internal.NewProgress(n, "Generating schema", internal.Verbose(), false, int(internal.SchemaCreationInProgress))
r := internal.NewReader(bufio.NewReader(f), p)
conv.SetSchemaMode() // Build schema and ignore data in dump.
Expand Down
8 changes: 8 additions & 0 deletions docs/data-types/mysql.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ and `ON UPDATE` actions, we drop them.
While Spanner supports default values, Spanner migration tool currently does not support translating source `DEFAULT` constraints to Spanner `DEFAULT` constraints. We drop the `DEFAULT` MySQL constraint during conversion.
It can be manually added to the DDL via an `ALTER TABLE` command.

## Check Constraints

While Spanner supports check constraints, the Spanner migration tool currently migrates all valid check constraints from MySQL to Spanner.

While moving from schema to prepare migration phase, if any invalid or unsupported check constraints are identified, the migration process will be halted and will not commence. This is to ensure the integrity and accuracy of the migration. Any identified issues will be clearly displayed in the Issues & Suggestions tab. This allows the user to review all errors in detail. The user will then have the opportunity to manually address these errors by either correcting the invalid check constraints or removing them altogether. This step is crucial to ensure that all constraints are compatible with the target system, thereby preventing potential issues during or after the migration process. Once the necessary corrections have been made, the user can proceed to re-initiate the migration process without any unsupported constraints.

> Note: As check constraints were introduced with MySQL version 8.0.16, the Spanner migration tool will automatically include these constraints in the Spanner draft for databases using this version or later. For MySQL versions prior to 8.0.16, where check constraints are not supported, users will need to manually incorporate any required check constraints into the Spanner draft. This approach ensures that all necessary constraints are accurately represented in the Spanner environment, tailored to the specific needs of the database.

## Secondary Indexes

The tool maps MySQL secondary indexes to Spanner secondary indexes, and preserves
Expand Down
2 changes: 1 addition & 1 deletion docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Please refer to the [issues section](https://github.com/GoogleCloudPlatform/span

- Schema Only Mode does not create foreign keys
- Foreign key actions such as [ON DELETE CASCADE](https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#create_table) are not supported. If you do not specify a foreign key action, Spanner infers NO ACTION as the default action
- Migration of check constraints, functions and views is not supported
- Migration of functions and views is not supported
- Schema recommendations are based on static analysis of the schema only
- PG Spanner dialect support is limited, and is not currently available on the UI

Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@ This example describes connecting to a CloudSQL source database using IP allowli

{: .important }
The same set of steps can be followed for any database which is accessible from Cloud shell. **Please note** this can not be used for databases within a VPC. If you want to connect to your database from within a VPC, we recommend
following [this guide](https://cloud.google.com/sql/docs/mysql/configure-private-services-access). Note that this approach will not use Cloud shell to run Harboourbridge. Instead, follow the [installation](https://googlecloudplatform.github.io/spanner-migration-tool/install.html#installing-spanner-migration-tool) to install Spanner migration tool on the compute instance in your VPC.
following [this guide](https://cloud.google.com/sql/docs/mysql/configure-private-services-access). Note that this approach will not use Cloud shell to run Spanner migration tool. Instead, follow the [installation](https://googlecloudplatform.github.io/spanner-migration-tool/install.html#installing-spanner-migration-tool) to install Spanner migration tool on the compute instance in your VPC.
2 changes: 1 addition & 1 deletion docs/troubleshoot/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ To bring up the Spanner Migration Tool UI, please follow the steps mentioned [he

### Can Spanner Migration Tool be used without connecting to the spanner instance?

Yes, Spanner Migration Tool can be used for schema assessment and modifications without connecting to the spanner instance
No, the Spanner Migration Tool cannot be used for schema assessment and modification without connecting to a Spanner instance. Support for default values and check constraints has been added, and this feature requires validation of DML, which means the Spanner instance must be up and running.

### When is a table interleavable?

Expand Down
11 changes: 10 additions & 1 deletion docs/ui/schema-conv/issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ nav_order: 3
# Issues and Suggestions
{: .no_toc }

Spanner migration tool scans through the generated spanner schema and notifies the user of any warnings encountered. It also makes intelligent suggestions to the user which would help them utilize the spanner capabilities to the fullest.
Spanner migration tool scans through the generated spanner schema and notifies the user of any warnings or errors encountered. It also makes intelligent suggestions to the user which would help them utilize the spanner capabilities to the fullest.

<details open markdown="block">
<summary>
Expand All @@ -29,6 +29,15 @@ Spanner migration tool scans through the generated spanner schema and notifies t
- [Hotspotting](https://cloud.google.com/spanner/docs/schema-design) due to timestamp or auto-increment keys
- Auto Increment has been converted to Sequence, set Ignore Range or Start with Counter to avoid duplicate value errors

## Errors

- Detection of unsupported check constraints in spanner.
- Data type mismatch of referenced column in check constraints.
- Referenced column in check constraints not found.
- Function referenced in check constraints is not found.

![](https://services.google.com/fh/files/misc/cc5.png)

## Suggestions

- Modifications related to converting a table into an interleaved one
Expand Down
1 change: 1 addition & 0 deletions docs/ui/schema-conv/schema-conv.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ In the configure schema page, the user can view details of the source database a
1. Primary Key
1. Foreign Key
1. Interleave
1. Check Constraints
1. SQL
1. Index details
1. Sequence Details (Only for Source DB MySql)
Expand Down
16 changes: 16 additions & 0 deletions docs/ui/schema-conv/spanner-draft.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ To interleave a `child table` within a `parent table`, the following conditions

4. Use the modified session file in the [SMT commands](../../cli/cli.md) or it can be imported via [SMT UI](../connect-source.md#load-session-file) and proceed further.

### Check Constraints
Users have the ability to view and modify check constraints of a table via the check constraints tab. They can alter the check constraint's name, condition, and even remove the check constraint entirely. Once these changes are made the [session file](../ui.md/#termsterminology) is updated.

![](https://services.google.com/fh/files/misc/cc3.png)

#### Add or Edit Constraint
Besides modifying the existing check constraint in the Spanner draft mapped from the source database, users can also add new constraints to the selected table.

![](https://services.google.com/fh/files/misc/cc2.png)

![](https://services.google.com/fh/files/misc/cc1.png)

#### Remove Constraint
In addition to adding check constraints, users can also remove the check constraints in the spanner draft for the selected table.

![](https://services.google.com/fh/files/misc/cc4.png)

### SQL

Expand Down
37 changes: 19 additions & 18 deletions sources/common/toddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,28 +192,29 @@ func (ss *SchemaToSpannerImpl) VerifyExpressions(conv *internal.Conv) error {
Source: conv.Source,
ExpressionDetailList: GenerateExpressionDetailList(spschema),
}

result := ss.ExpressionVerificationAccessor.VerifyExpressions(ctx, verifyExpressionsInput)
if result.ExpressionVerificationOutputList == nil {
return result.Err
}
issueTypes := GetIssue(result)
if len(issueTypes) > 0 {
for tableId, issues := range issueTypes {

for _, issue := range issues {
if _, exists := conv.SchemaIssues[tableId]; !exists {
conv.SchemaIssues[tableId] = internal.TableIssues{
TableLevelIssues: []internal.SchemaIssue{},
if len(verifyExpressionsInput.ExpressionDetailList) != 0 {
result := ss.ExpressionVerificationAccessor.VerifyExpressions(ctx, verifyExpressionsInput)
if result.ExpressionVerificationOutputList == nil {
return result.Err
}
issueTypes := GetIssue(result)
if len(issueTypes) > 0 {
for tableId, issues := range issueTypes {

for _, issue := range issues {
if _, exists := conv.SchemaIssues[tableId]; !exists {
conv.SchemaIssues[tableId] = internal.TableIssues{
TableLevelIssues: []internal.SchemaIssue{},
}
}
}

tableIssue := conv.SchemaIssues[tableId]
tableIssue := conv.SchemaIssues[tableId]

if !IsSchemaIssuePresent(tableIssue.TableLevelIssues, issue) {
tableIssue.TableLevelIssues = append(tableIssue.TableLevelIssues, issue)
if !IsSchemaIssuePresent(tableIssue.TableLevelIssues, issue) {
tableIssue.TableLevelIssues = append(tableIssue.TableLevelIssues, issue)
}
conv.SchemaIssues[tableId] = tableIssue
}
conv.SchemaIssues[tableId] = tableIssue
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions sources/mysql/infoschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,13 +344,24 @@ func (isi InfoSchemaImpl) processRow(
// Case added to handle check constraints
case "CHECK":
checkClause = collationRegex.ReplaceAllString(checkClause, "")
checkClause = checkAndAddParentheses(checkClause)
*checkKeys = append(*checkKeys, schema.CheckConstraint{Name: constraintName, Expr: checkClause, ExprId: internal.GenerateExpressionId(), Id: internal.GenerateCheckConstrainstId()})
default:
m[col] = append(m[col], constraintType)
}
return nil
}

// checkAndAddParentheses this method will check parentheses if found it will return same string
// or add the parentheses then return the string
func checkAndAddParentheses(checkClause string) string {
if strings.HasPrefix(checkClause, "(") && strings.HasSuffix(checkClause, ")") {
return checkClause
} else {
return `(` + checkClause + `)`
}
}

// GetForeignKeys return list all the foreign keys constraints.
// MySQL supports cross-database foreign key constraints. We ignore
// them because the Spanner migration tool works database at a time (a specific run
Expand Down
1 change: 1 addition & 0 deletions sources/mysql/mysqldump.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ func getCheckConstraints(constraints []*ast.Constraint) (checkConstraints []sche
if constraint.Tp == ast.ConstraintCheck {
exp := expressionToString(constraint.Expr)
exp = dbcollationRegex.ReplaceAllString(exp, "$1")
exp = checkAndAddParentheses(exp)
checkConstraint := schema.CheckConstraint{
Name: constraint.Name,
Expr: exp,
Expand Down
71 changes: 71 additions & 0 deletions test_data/mysql_checkconstraint_dump.test.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
-- MySQL dump 10.13 Distrib 8.3.0, for macos13.6 (arm64)
--
-- Host: 127.0.0.1 Database: test_mysql_checkconstraint
-- ------------------------------------------------------
-- Server version 8.0.40

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Current Database: `test_mysql_checkconstraint`
--

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `test_mysql_checkconstraint` /*!40100 DEFAULT CHARACTER SET utf8mb4 */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `test_mysql_checkconstraint`;

--
-- Table structure for table `TestTable`
--

DROP TABLE IF EXISTS `TestTable`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `TestTable` (
`ID` bigint NOT NULL,
`Value` bigint DEFAULT NULL,
`Flag` tinyint(1) DEFAULT NULL,
`Date` timestamp NULL DEFAULT NULL,
`Name` varchar(255) DEFAULT NULL,
`EnumValue` enum('OptionA','OptionB','OptionC') DEFAULT NULL,
`BooleanValue` tinyint DEFAULT NULL,
PRIMARY KEY (`ID`),
CONSTRAINT `chk_bitwise` CHECK (((`Value` & 2) = 0)),
CONSTRAINT `chk_Boolean` CHECK ((`BooleanValue` in (0,1))),
CONSTRAINT `chk_DateRange` CHECK ((`Date` between '2000-01-01 00:00:00' and '2100-12-31 23:59:59')),
CONSTRAINT `chk_Enum` CHECK ((`EnumValue` in ('OptionA','OptionB','OptionC'))),
CONSTRAINT `chk_NullValue` CHECK ((`Value` is not null)),
CONSTRAINT `chk_range` CHECK (((`Value` > 10) and (`Value` < 1000))),
CONSTRAINT `chk_StringLength` CHECK ((char_length(`Name`) > 5))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `TestTable`
--

LOCK TABLES `TestTable` WRITE;
/*!40000 ALTER TABLE `TestTable` DISABLE KEYS */;
INSERT INTO `TestTable` VALUES (1,12,1,'2023-10-08 03:30:00','ExampleName','OptionA',1);
/*!40000 ALTER TABLE `TestTable` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2025-01-08 16:06:14
Loading
Loading