Added verify-constraints command

This commit is contained in:
Daylon Wilkins
2020-12-10 16:10:55 -08:00
committed by Daylon Wilkins
parent 331a5a6ab5
commit 293be61d02
3 changed files with 214 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bats
load $BATS_TEST_DIRNAME/helper/common.bash
setup() {
setup_common
dolt sql <<SQL
CREATE TABLE parent1 (
pk BIGINT PRIMARY KEY,
v1 BIGINT,
INDEX (v1)
);
CREATE TABLE parent2 (
pk BIGINT PRIMARY KEY,
v1 BIGINT,
INDEX (v1)
);
CREATE TABLE child1 (
pk BIGINT PRIMARY KEY,
parent1_v1 BIGINT,
parent2_v1 BIGINT,
CONSTRAINT child1_parent1 FOREIGN KEY (parent1_v1) REFERENCES parent1 (v1),
CONSTRAINT child1_parent2 FOREIGN KEY (parent2_v1) REFERENCES parent2 (v1)
);
CREATE TABLE child2 (
pk BIGINT PRIMARY KEY,
parent2_v1 BIGINT,
CONSTRAINT child2_parent2 FOREIGN KEY (parent2_v1) REFERENCES parent2 (v1)
);
INSERT INTO parent1 VALUES (1,1), (2,2), (3,3);
INSERT INTO parent2 VALUES (1,1), (2,2), (3,3);
INSERT INTO child1 VALUES (1,1,1), (2,2,2);
INSERT INTO child2 VALUES (2,2), (3,3);
SQL
}
teardown() {
teardown_common
}
@test "verify-constraints: Constraints verified" {
dolt verify-constraints child1 child2
}
@test "verify-constraints: One table fails" {
dolt sql <<SQL
SET foreign_key_checks=0;
DELETE FROM parent1 WHERE pk = 1;
SET foreign_key_checks=1;
SQL
run dolt verify-constraints child1
[ "$status" -eq "1" ]
[[ "$output" =~ "child1_parent1" ]] || false
dolt verify-constraints child2
run dolt verify-constraints child1 child2
[ "$status" -eq "1" ]
[[ "$output" =~ "child1_parent1" ]] || false
[[ ! "$output" =~ "child1_parent2" ]] || false
[[ ! "$output" =~ "child2_parent2" ]] || false
}
@test "verify-constraints: Two tables fail" {
dolt sql <<SQL
SET foreign_key_checks=0;
DELETE FROM parent2 WHERE pk = 2;
SET foreign_key_checks=1;
SQL
run dolt verify-constraints child1
[ "$status" -eq "1" ]
[[ "$output" =~ "child1_parent2" ]] || false
[[ ! "$output" =~ "child1_parent1" ]] || false
run dolt verify-constraints child2
[ "$status" -eq "1" ]
[[ "$output" =~ "child2_parent2" ]] || fals
run dolt verify-constraints child1 child2
[ "$status" -eq "1" ]
[[ "$output" =~ "child1_parent2" ]] || false
[[ "$output" =~ "child2_parent2" ]] || false
[[ ! "$output" =~ "child1_parent1" ]] || false
}

View File

@@ -0,0 +1,134 @@
// Copyright 2020 Dolthub, Inc.
//
// 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 commands
import (
"context"
"errors"
"strings"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/table"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
)
var verifyConstraintsDocs = cli.CommandDocumentationContent{
ShortDesc: `Verifies a table's constraints'`,
LongDesc: `This command verifies that the defined constraints on the given table(s)—such as a foreign key—are correct and satisfied.`,
Synopsis: []string{`{{.LessThan}}table{{.GreaterThan}}...`},
}
type VerifyConstraintsCmd struct{}
var _ cli.Command = VerifyConstraintsCmd{}
var _ cli.HiddenCommand = VerifyConstraintsCmd{}
func (cmd VerifyConstraintsCmd) Name() string {
return "verify-constraints"
}
func (cmd VerifyConstraintsCmd) Description() string {
return "Command to verify that the constraints on the given table(s) are satisfied."
}
func (cmd VerifyConstraintsCmd) CreateMarkdown(fs filesys.Filesys, path, commandStr string) error {
return nil
}
func (cmd VerifyConstraintsCmd) createArgParser() *argparser.ArgParser {
ap := argparser.NewArgParser()
ap.ArgListHelp = append(ap.ArgListHelp, [2]string{"table", "The table to check constraints on."})
return ap
}
func (cmd VerifyConstraintsCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv) int {
ap := cmd.createArgParser()
help, usage := cli.HelpAndUsagePrinters(cli.GetCommandDocumentation(commandStr, verifyConstraintsDocs, ap))
apr := cli.ParseArgs(ap, args, help)
if apr.NArg() == 0 {
usage()
return 0
}
tableNames := apr.Args()
working, err := dEnv.WorkingRoot(ctx)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get working.").AddCause(err).Build(), nil)
}
fkColl, err := working.GetForeignKeyCollection(ctx)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get foreign keys.").AddCause(err).Build(), nil)
}
var accumulatedConstraintErrors []string
for _, givenTableName := range tableNames {
tbl, tableName, ok, err := working.GetTableInsensitive(ctx, givenTableName)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get table %s.", givenTableName).AddCause(err).Build(), nil)
}
if !ok {
return HandleVErrAndExitCode(errhand.BuildDError("Table %s does not exist.", givenTableName).Build(), nil)
}
tblSch, err := tbl.GetSchema(ctx)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get schema for %s.", tableName).AddCause(err).Build(), nil)
}
fks, _ := fkColl.KeysForTable(tableName)
for _, fk := range fks {
childIdx := tblSch.Indexes().GetByName(fk.TableIndex)
childIdxRowData, err := tbl.GetIndexRowData(ctx, fk.TableIndex)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get index data for %s.", fk.TableIndex).AddCause(err).Build(), nil)
}
parentTbl, _, ok, err := working.GetTableInsensitive(ctx, fk.ReferencedTableName)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get table %s.", fk.ReferencedTableName).AddCause(err).Build(), nil)
}
if !ok {
return HandleVErrAndExitCode(errhand.BuildDError("Table %s does not exist.", fk.ReferencedTableName).Build(), nil)
}
parentTblSch, err := parentTbl.GetSchema(ctx)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get schema for %s.", fk.ReferencedTableName).AddCause(err).Build(), nil)
}
parentIdx := parentTblSch.Indexes().GetByName(fk.ReferencedTableIndex)
parentIdxRowData, err := parentTbl.GetIndexRowData(ctx, fk.ReferencedTableIndex)
if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Unable to get index data for %s.", fk.ReferencedTableIndex).AddCause(err).Build(), nil)
}
err = table.ForeignKeyIsSatisfied(ctx, fk, childIdxRowData, parentIdxRowData, childIdx, parentIdx)
if err != nil {
accumulatedConstraintErrors = append(accumulatedConstraintErrors, err.Error())
}
}
}
if len(accumulatedConstraintErrors) > 0 {
dErr := errhand.BuildDError("All constraints are not satisfied.")
dErr = dErr.AddCause(errors.New(strings.Join(accumulatedConstraintErrors, "\n")))
return HandleVErrAndExitCode(dErr.Build(), nil)
}
return 0
}
func (cmd VerifyConstraintsCmd) Hidden() bool {
return true
}

View File

@@ -84,6 +84,7 @@ var doltCommand = cli.NewSubCommandHandler("dolt", "it's git for data", []cli.Co
commands.ReadTablesCmd{},
commands.GarbageCollectionCmd{},
commands.FilterBranchCmd{},
commands.VerifyConstraintsCmd{},
})
func init() {