diff --git a/yb-voyager/cmd/importSchema.go b/yb-voyager/cmd/importSchema.go index 69fdc9be4..dd053a086 100644 --- a/yb-voyager/cmd/importSchema.go +++ b/yb-voyager/cmd/importSchema.go @@ -225,8 +225,14 @@ func importSchema() error { dumpStatements(finalFailedSqlStmts, filepath.Join(exportDir, "schema", "failed.sql")) } - if flagPostSnapshotImport && flagRefreshMViews { - refreshMViews(conn) + if flagPostSnapshotImport { + err = importSchemaInternal(exportDir, []string{"TABLE"}, nil) + if err != nil { + return err + } + if flagRefreshMViews { + refreshMViews(conn) + } } else { utils.PrintAndLog("\nNOTE: Materialized Views are not populated by default. To populate them, pass --refresh-mviews while executing `import schema --post-snapshot-import`.") } diff --git a/yb-voyager/cmd/importSchemaYugabyteDB.go b/yb-voyager/cmd/importSchemaYugabyteDB.go index ec311172d..e1f5c5a33 100644 --- a/yb-voyager/cmd/importSchemaYugabyteDB.go +++ b/yb-voyager/cmd/importSchemaYugabyteDB.go @@ -27,6 +27,7 @@ import ( log "github.com/sirupsen/logrus" "golang.org/x/exp/slices" + "github.com/yugabyte/yb-voyager/yb-voyager/src/queryparser" "github.com/yugabyte/yb-voyager/yb-voyager/src/utils" ) @@ -49,6 +50,21 @@ func importSchemaInternal(exportDir string, importObjectList []string, return nil } +func isNotValidConstraint(stmt string) (bool, error) { + ddlObj, err := queryparser.ParseAndProcessDDL(stmt) + if err != nil { + return false, fmt.Errorf("error in parsing and process DDL[%s]:%v", stmt, err) + } + alter, ok := ddlObj.(*queryparser.AlterTable) + if !ok { + return false, nil + } + if alter.IsAddConstraintType() && alter.ConstraintNotValid { + return true, nil + } + return false, nil +} + func executeSqlFile(file string, objType string, skipFn func(string, string) bool) error { log.Infof("Execute SQL file %q on target %q", file, tconf.Host) conn := newTargetConn() @@ -74,10 +90,13 @@ func executeSqlFile(file string, objType string, skipFn func(string, string) boo if objType == "TABLE" { stmt := strings.ToUpper(sqlInfo.stmt) - skip := strings.Contains(stmt, "ALTER TABLE") && strings.Contains(stmt, "REPLICA IDENTITY") + // Check if the statement should be skipped + skip, err := shouldSkipDDL(stmt) + if err != nil { + return fmt.Errorf("error checking whether to skip DDL: %v", err) + } if skip { - //skipping DDLS like ALTER TABLE ... REPLICA IDENTITY .. as this is not supported in YB - log.Infof("Skipping DDL: %s", sqlInfo.stmt) + log.Infof("Skipping DDL: %s", stmt) continue } } @@ -90,6 +109,27 @@ func executeSqlFile(file string, objType string, skipFn func(string, string) boo return nil } +func shouldSkipDDL(stmt string) (bool, error) { + skipReplicaIdentity := strings.Contains(stmt, "ALTER TABLE") && strings.Contains(stmt, "REPLICA IDENTITY") + isNotValid, err := isNotValidConstraint(stmt) + if err != nil { + return false, fmt.Errorf("error checking whether stmt is to add not valid constraint: %v", stmt, err) + } + if skipReplicaIdentity { + //skipping DDLS like ALTER TABLE ... REPLICA IDENTITY .. as this is not supported in YB + log.Infof("Skipping DDL: %s", stmt) + return true, nil + } + if isNotValid && !bool(flagPostSnapshotImport) { + //Skipping the ALTER TABLE ... ADD CONSTRAINT ... NOT VALID DDLs in import schema without post-snapshot-import flag + return true, nil + } + if bool(flagPostSnapshotImport) && !isNotValid { + return true, nil + } + return false, nil +} + func executeSqlStmtWithRetries(conn **pgx.Conn, sqlInfo sqlInfo, objType string) error { var err error log.Infof("On %s run query:\n%s\n", tconf.Host, sqlInfo.formattedStmt) diff --git a/yb-voyager/src/queryparser/ddl_processor.go b/yb-voyager/src/queryparser/ddl_processor.go index 4f0956192..ac847e11f 100644 --- a/yb-voyager/src/queryparser/ddl_processor.go +++ b/yb-voyager/src/queryparser/ddl_processor.go @@ -555,11 +555,17 @@ func (atProcessor *AlterTableProcessor) Process(parseTree *pg_query.ParseResult) similar to CREATE table 2nd case where constraint is at the end of column definitions mentioning the constraint only so here as well while adding constraint checking the type of constraint and the deferrable field of it. + + ALTER TABLE test ADD CONSTRAINT chk check (id<>'') NOT VALID; + stmts:{stmt:...subtype:AT_AddConstraint def:{constraint:{contype:CONSTR_CHECK conname:"chk" location:22 + raw_expr:{a_expr:{kind:AEXPR_OP name:{string:{sval:"<>"}} lexpr:{column_ref:{fields:{string:{sval:"id"}} location:43}} rexpr:{a_const:{sval:{} + location:47}} location:45}} skip_validation:true}} behavior:DROP_RESTRICT}} objtype:OBJECT_TABLE}} stmt_len:60} */ constraint := cmd.GetDef().GetConstraint() alter.ConstraintType = constraint.Contype alter.ConstraintName = constraint.Conname alter.IsDeferrable = constraint.Deferrable + alter.ConstraintNotValid = constraint.SkipValidation // this is set for the NOT VALID clause alter.ConstraintColumns = parseColumnsFromKeys(constraint.GetKeys()) case pg_query.AlterTableType_AT_DisableRule: @@ -590,10 +596,11 @@ type AlterTable struct { NumSetAttributes int NumStorageOptions int //In case AlterType - ADD_CONSTRAINT - ConstraintType pg_query.ConstrType - ConstraintName string - IsDeferrable bool - ConstraintColumns []string + ConstraintType pg_query.ConstrType + ConstraintName string + ConstraintNotValid bool + IsDeferrable bool + ConstraintColumns []string } func (a *AlterTable) GetObjectName() string { @@ -606,6 +613,10 @@ func (a *AlterTable) AddPrimaryKeyOrUniqueCons() bool { return a.ConstraintType == PRIMARY_CONSTR_TYPE || a.ConstraintType == UNIQUE_CONSTR_TYPE } +func (a *AlterTable) IsAddConstraintType() bool { + return a.AlterType == pg_query.AlterTableType_AT_AddConstraint +} + //===========POLICY PROCESSOR ================================ // PolicyProcessor handles parsing CREATE POLICY statements @@ -869,5 +880,3 @@ var deferrableConstraintsList = []pg_query.ConstrType{ pg_query.ConstrType_CONSTR_ATTR_DEFERRED, pg_query.ConstrType_CONSTR_ATTR_IMMEDIATE, } - -