diff --git a/go/cmd/dolt/commands/diff.go b/go/cmd/dolt/commands/diff.go index 8e594b44382..2ffd637c2e6 100644 --- a/go/cmd/dolt/commands/diff.go +++ b/go/cmd/dolt/commands/diff.go @@ -385,14 +385,14 @@ func getTableNamesAtRef(queryist cli.Queryist, sqlCtx *sql.Context, ref string) if err != nil { return nil, fmt.Errorf("error interpolating query: %w", err) } - _, err = GetRowsForSql(queryist, sqlCtx, interpolatedQuery) - if err == nil { - tableNames[sysTable] = true - } else if isTableNotFoundError(err) { - continue - } else { + result, err := GetRowsForSql(queryist, sqlCtx, interpolatedQuery) + if err != nil { return nil, fmt.Errorf("error getting system table %s: %w", sysTable, err) } + + if len(result) > 0 { + tableNames[sysTable] = true + } } return tableNames, nil diff --git a/go/libraries/doltcore/doltdb/system_table.go b/go/libraries/doltcore/doltdb/system_table.go index cc60da73dc5..87d59e57b29 100644 --- a/go/libraries/doltcore/doltdb/system_table.go +++ b/go/libraries/doltcore/doltdb/system_table.go @@ -236,9 +236,6 @@ const ( const ( // SchemasTableName is the name of the dolt schema fragment table SchemasTableName = "dolt_schemas" - // SchemasTablesIdCol is an incrementing integer that represents the insertion index. - // Deprecated: This column is no longer used and will be removed in a future release. - SchemasTablesIdCol = "id" // SchemasTablesTypeCol is the name of the column that stores the type of a schema fragment in the dolt_schemas table SchemasTablesTypeCol = "type" // SchemasTablesNameCol The name of the column that stores the name of a schema fragment in the dolt_schemas table diff --git a/go/libraries/doltcore/sqle/database.go b/go/libraries/doltcore/sqle/database.go index 9d2e80ee71c..7f254221ea8 100644 --- a/go/libraries/doltcore/sqle/database.go +++ b/go/libraries/doltcore/sqle/database.go @@ -499,6 +499,33 @@ func (db Database) getTableInsensitive(ctx *sql.Context, head *doltdb.Commit, ds } case doltdb.StatisticsTableName: dt, found = dtables.NewStatisticsTable(ctx, db.Name(), db.ddb, asOf), true + case doltdb.ProceduresTableName: + found = true + backingTable, _, err := db.getTable(ctx, root, doltdb.ProceduresTableName) + if err != nil { + return nil, false, err + } + if backingTable == nil { + dt = NewEmptyProceduresTable() + } else { + writeTable := backingTable.(*WritableDoltTable) + dt = NewProceduresTable(writeTable) + } + case doltdb.SchemasTableName: + found = true + backingTable, _, err := db.getTable(ctx, root, doltdb.SchemasTableName) + if err != nil { + return nil, false, err + } + if backingTable == nil { + dt = NewEmptySchemaTable() + } else { + writeTable := backingTable.(*WritableDoltTable) + dt, err = NewSchemaTable(ctx, db, writeTable) + if err != nil { + return nil, false, err + } + } } if found { @@ -1407,16 +1434,22 @@ func (db Database) GetViewDefinition(ctx *sql.Context, viewName string) (sql.Vie } } - tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return sql.ViewDefinition{}, false, err } + + wrapper, ok := tbl.(*SchemaTable) if !ok { + return sql.ViewDefinition{}, false, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + + if wrapper.backingTable == nil { dbState.SessionCache().CacheViews(key, nil, db.schemaName) return sql.ViewDefinition{}, false, nil } - views, viewDef, found, err := getViewDefinitionFromSchemaFragmentsOfView(ctx, tbl.(*WritableDoltTable), viewName) + views, viewDef, found, err := getViewDefinitionFromSchemaFragmentsOfView(ctx, wrapper.backingTable, viewName) if err != nil { return sql.ViewDefinition{}, false, err } @@ -1464,15 +1497,20 @@ func getViewDefinitionFromSchemaFragmentsOfView(ctx *sql.Context, tbl *WritableD // AllViews implements sql.ViewDatabase func (db Database) AllViews(ctx *sql.Context) ([]sql.ViewDefinition, error) { - tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return nil, err } + + wrapper, ok := tbl.(*SchemaTable) if !ok { + return nil, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + if wrapper.backingTable == nil { return nil, nil } - views, _, _, err := getViewDefinitionFromSchemaFragmentsOfView(ctx, tbl.(*WritableDoltTable), "") + views, _, _, err := getViewDefinitionFromSchemaFragmentsOfView(ctx, wrapper.backingTable, "") if err != nil { return nil, err } @@ -1524,15 +1562,21 @@ func (db Database) GetTriggers(ctx *sql.Context) ([]sql.TriggerDefinition, error dbState.SessionCache().CacheTriggers(key, triggers, db.schemaName) }() - tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return nil, err } + + wrapper, ok := tbl.(*SchemaTable) if !ok { + return nil, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + + if wrapper.backingTable == nil { return nil, nil } - frags, err := getSchemaFragmentsOfType(ctx, tbl.(*WritableDoltTable), triggerFragment) + frags, err := getSchemaFragmentsOfType(ctx, wrapper.backingTable, triggerFragment) if err != nil { return nil, err } @@ -1571,15 +1615,20 @@ func (db Database) DropTrigger(ctx *sql.Context, name string) error { // GetEvent implements sql.EventDatabase. func (db Database) GetEvent(ctx *sql.Context, name string) (sql.EventDefinition, bool, error) { - tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return sql.EventDefinition{}, false, err } + + wrapper, ok := tbl.(*SchemaTable) if !ok { + return sql.EventDefinition{}, false, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + if wrapper.backingTable == nil { return sql.EventDefinition{}, false, nil } - frags, err := getSchemaFragmentsOfType(ctx, tbl.(*WritableDoltTable), eventFragment) + frags, err := getSchemaFragmentsOfType(ctx, wrapper.backingTable, eventFragment) if err != nil { return sql.EventDefinition{}, false, err } @@ -1598,16 +1647,21 @@ func (db Database) GetEvent(ctx *sql.Context, name string) (sql.EventDefinition, // GetEvents implements sql.EventDatabase. func (db Database) GetEvents(ctx *sql.Context) (events []sql.EventDefinition, token interface{}, err error) { - tbl, ok, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return nil, nil, err } + wrapper, ok := tbl.(*SchemaTable) if !ok { + return nil, nil, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + + if wrapper.backingTable == nil { // If the dolt_schemas table doesn't exist, it's not an error, just no events return nil, nil, nil } - frags, err := getSchemaFragmentsOfType(ctx, tbl.(*WritableDoltTable), eventFragment) + frags, err := getSchemaFragmentsOfType(ctx, wrapper.backingTable, eventFragment) if err != nil { return nil, nil, err } @@ -1844,15 +1898,20 @@ func (db Database) dropFragFromSchemasTable(ctx *sql.Context, fragType, name str db.schemaName = schemaName } - stbl, found, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + stbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return err } - if !found { + + swrapper, ok := stbl.(*SchemaTable) + if !ok { + return fmt.Errorf("expected a SchemaTable, but found %T", stbl) + } + if swrapper.backingTable == nil { return missingErr } - tbl := stbl.(*WritableDoltTable) + tbl := swrapper.backingTable row, exists, err := fragFromSchemasTable(ctx, tbl, fragType, name) if err != nil { return err @@ -1886,6 +1945,13 @@ func (db Database) dropTableIfEmpty(ctx *sql.Context, tableName string) error { return nil } + if wrapped, ok := stbl.(WritableDoltTableWrapper); ok { + stbl = wrapped.UnWrap() + if stbl == nil { + return nil + } + } + table, err := stbl.(*WritableDoltTable).DoltTable.DoltTable(ctx) if err != nil { return err diff --git a/go/libraries/doltcore/sqle/procedures_table.go b/go/libraries/doltcore/sqle/procedures_table.go index 39c2b25d64f..89e49ff83cf 100644 --- a/go/libraries/doltcore/sqle/procedures_table.go +++ b/go/libraries/doltcore/sqle/procedures_table.go @@ -25,6 +25,7 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/schema" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" "github.com/dolthub/dolt/go/store/types" @@ -43,6 +44,81 @@ const ( ProceduresTableModifiedAtCol = "modified_at" ) +type ProceduresTable struct { + backingTable *WritableDoltTable +} + +func (pt *ProceduresTable) Name() string { + return ProceduresTableName +} + +func (pt *ProceduresTable) String() string { + return ProceduresTableName +} + +func (pt *ProceduresTable) Schema() sql.Schema { + return ProceduresTableSqlSchema().Schema +} + +func (pt *ProceduresTable) Collation() sql.CollationID { + return sql.Collation_Default +} + +func (pt *ProceduresTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { + if pt.backingTable == nil { + // no backing table; return an empty iter. + return index.SinglePartitionIterFromNomsMap(nil), nil + } + return pt.backingTable.Partitions(ctx) +} + +func (pt *ProceduresTable) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) { + if pt.backingTable == nil { + // no backing table; return an empty iter. + return sql.RowsToRowIter(), nil + } + + return pt.backingTable.PartitionRows(ctx, partition) +} + +func (pt *ProceduresTable) LockedToRoot(ctx *sql.Context, root doltdb.RootValue) (sql.IndexAddressableTable, error) { + if pt.backingTable == nil { + return pt, nil + + } + return pt.backingTable.LockedToRoot(ctx, root) +} + +func (pt *ProceduresTable) IndexedAccess(lookup sql.IndexLookup) sql.IndexedTable { + // Never reached. Interface required for LockedToRoot to be implemented. + panic("Unreachable") +} + +func (pt *ProceduresTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { + return nil, nil +} + +func (pt *ProceduresTable) PreciseMatch() bool { + return true +} + +func (pt *ProceduresTable) UnWrap() *WritableDoltTable { + return pt.backingTable +} + +func NewProceduresTable(backing *WritableDoltTable) sql.Table { + return &ProceduresTable{backingTable: backing} +} + +func NewEmptyProceduresTable() sql.Table { + return &ProceduresTable{} +} + +var _ sql.Table = (*ProceduresTable)(nil) +var _ dtables.VersionableTable = (*ProceduresTable)(nil) +var _ sql.IndexAddressableTable = (*ProceduresTable)(nil) +var _ WritableDoltTableWrapper = (*ProceduresTable)(nil) + // The fixed SQL schema for the `dolt_procedures` table. func ProceduresTableSqlSchema() sql.PrimaryKeySchema { sqlSchema, err := sqlutil.FromDoltSchema("", doltdb.ProceduresTableName, ProceduresTableSchema()) @@ -65,37 +141,47 @@ func ProceduresTableSchema() schema.Schema { } // DoltProceduresGetOrCreateTable returns the `dolt_procedures` table from the given db, creating it in the db's -// current root if it doesn't exist +// current root if it doesn't exist. func DoltProceduresGetOrCreateTable(ctx *sql.Context, db Database) (*WritableDoltTable, error) { tbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) if err != nil { return nil, err } - if found { - // Make sure the schema is up to date - writableDoltTable := tbl.(*WritableDoltTable) - return migrateDoltProceduresSchema(ctx, db, writableDoltTable) - } - - root, err := db.GetRoot(ctx) - if err != nil { - return nil, err + if !found { + // Should never happen. + panic("runtime error. dolt_procedures table not found") } - err = db.createDoltTable(ctx, doltdb.ProceduresTableName, doltdb.DefaultSchemaName, root, ProceduresTableSchema()) - if err != nil { - return nil, err + wrapper, ok := tbl.(*ProceduresTable) + if !ok { + return nil, fmt.Errorf("expected a ProceduresTable, but got %T", tbl) } - tbl, found, err = db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) - if err != nil { - return nil, err - } - // Verify it was created successfully - if !found { - return nil, sql.ErrTableNotFound.New(ProceduresTableName) + if wrapper.backingTable == nil { + // We haven't materialized the table yet. Go ahead and do so. + root, err := db.GetRoot(ctx) + if err != nil { + return nil, err + } + err = db.createDoltTable(ctx, doltdb.ProceduresTableName, doltdb.DefaultSchemaName, root, ProceduresTableSchema()) + if err != nil { + return nil, err + } + tbl, _, err = db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) + if err != nil { + return nil, err + } + wrapper, ok = tbl.(*ProceduresTable) + if !ok { + return nil, fmt.Errorf("expected a ProceduresTable, but got %T", tbl) + } + if wrapper.backingTable == nil { + return nil, sql.ErrTableNotFound.New(ProceduresTableName) + } + return wrapper.backingTable, nil + } else { + return migrateDoltProceduresSchema(ctx, db, wrapper.backingTable) } - return tbl.(*WritableDoltTable), nil } // migrateDoltProceduresSchema migrates the dolt_procedures system table from a previous schema version to the current @@ -162,15 +248,20 @@ func migrateDoltProceduresSchema(ctx *sql.Context, db Database, oldTable *Writab return nil, err } - tbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) if err != nil { return nil, err } - if !found { + + wrapper, ok := tbl.(*ProceduresTable) + if !ok { + return nil, fmt.Errorf("expected a ProceduresTable, but got %T", tbl) + } + if wrapper.backingTable == nil { return nil, sql.ErrTableNotFound.New(doltdb.ProceduresTableName) } - inserter := tbl.(*WritableDoltTable).Inserter(ctx) + inserter := wrapper.backingTable.Inserter(ctx) for _, row := range newRows { err = inserter.Insert(ctx, row) if err != nil { @@ -183,22 +274,24 @@ func migrateDoltProceduresSchema(ctx *sql.Context, db Database, oldTable *Writab return nil, err } - return tbl.(*WritableDoltTable), nil + return wrapper.backingTable, nil } // DoltProceduresGetTable returns the `dolt_procedures` table from the given db, or nil if the table doesn't exist func DoltProceduresGetTable(ctx *sql.Context, db Database) (*WritableDoltTable, error) { - tbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) if err != nil { return nil, err } - if found { - // Make sure the schema is up to date - writableDoltTable := tbl.(*WritableDoltTable) - return migrateDoltProceduresSchema(ctx, db, writableDoltTable) - } else { - return nil, nil + + wrapper, ok := tbl.(*ProceduresTable) + if !ok { + return nil, fmt.Errorf("expected a ProceduresTable, but got %T", tbl) + } + if wrapper.backingTable != nil { + return migrateDoltProceduresSchema(ctx, db, wrapper.backingTable) } + return nil, nil } // DoltProceduresGetAll returns all stored procedures for the database if the procedureName is blank (and empty string), diff --git a/go/libraries/doltcore/sqle/procedures_table_test.go b/go/libraries/doltcore/sqle/procedures_table_test.go index 89dbcfe3e70..13cb86abd24 100644 --- a/go/libraries/doltcore/sqle/procedures_table_test.go +++ b/go/libraries/doltcore/sqle/procedures_table_test.go @@ -67,7 +67,12 @@ func TestProceduresMigration(t *testing.T) { tbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) require.NoError(t, err) require.True(t, found) - rows := readAllRows(ctx, t, tbl.(*WritableDoltTable)) + + wrapper, ok := tbl.(*ProceduresTable) + require.True(t, ok) + require.NotNil(t, wrapper.backingTable) + + rows := readAllRows(ctx, t, wrapper.backingTable) expectedRows := []sql.Row{ {"proc1", "create procedure proc1() SELECT 42 as pk from dual;", timestamp, timestamp, nil}, {"proc2", "create procedure proc2() SELECT 'HELLO' as greeting from dual;", timestamp, timestamp, nil}, @@ -91,7 +96,12 @@ func TestProceduresMigration(t *testing.T) { tbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) require.NoError(t, err) require.True(t, found) - rows := readAllRows(ctx, t, tbl.(*WritableDoltTable)) + + wrapper, ok := tbl.(*ProceduresTable) + require.True(t, ok) + require.NotNil(t, wrapper.backingTable) + + rows := readAllRows(ctx, t, wrapper.backingTable) expectedRows := []sql.Row{ {"proc1", "create procedure proc1() SELECT 42 as pk from dual;", timestamp, timestamp, nil}, {"proc2", "create procedure proc2() SELECT 'HELLO' as greeting from dual;", timestamp, timestamp, nil}, @@ -122,8 +132,12 @@ func newDatabaseWithProcedures(t *testing.T, dEnv *env.DoltEnv, opts editor.Opti require.NoError(t, err) require.True(t, found) + wrapper, ok := sqlTbl.(*ProceduresTable) + require.True(t, ok) + require.NotNil(t, wrapper.backingTable) + // Insert some test data for procedures - inserter := sqlTbl.(*WritableDoltTable).Inserter(ctx) + inserter := wrapper.backingTable.Inserter(ctx) require.NoError(t, inserter.Insert(ctx, sql.Row{"proc1", "create procedure proc1() SELECT 42 as pk from dual;", timestamp, timestamp})) require.NoError(t, inserter.Insert(ctx, sql.Row{"proc2", "create procedure proc2() SELECT 'HELLO' as greeting from dual;", timestamp, timestamp})) require.NoError(t, inserter.Close(ctx)) diff --git a/go/libraries/doltcore/sqle/schema_table.go b/go/libraries/doltcore/sqle/schema_table.go index 46d4544a751..e3b20ed7083 100644 --- a/go/libraries/doltcore/sqle/schema_table.go +++ b/go/libraries/doltcore/sqle/schema_table.go @@ -27,7 +27,10 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/schema" "github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/resolve" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" ) const ( @@ -40,6 +43,93 @@ type Extra struct { CreatedAt int64 } +type SchemaTable struct { + backingTable *WritableDoltTable +} + +func (st *SchemaTable) Name() string { + return doltdb.SchemasTableName +} + +func (st *SchemaTable) String() string { + return doltdb.SchemasTableName +} + +func (st *SchemaTable) Schema() sql.Schema { + if st.backingTable == nil { + // No backing table; return an current schema. + return SchemaTableSqlSchema().Schema + } + + if !st.backingTable.Schema().Contains(doltdb.SchemasTablesSqlModeCol, doltdb.SchemasTableName) { + // No SQL_MODE column; return an old schema. + return SchemaTableV1SqlSchema() + } + + return SchemaTableSqlSchema().Schema +} + +func (st *SchemaTable) Collation() sql.CollationID { + return sql.Collation_Default +} + +func (st *SchemaTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { + if st.backingTable == nil { + return index.SinglePartitionIterFromNomsMap(nil), nil + } + return st.backingTable.Partitions(ctx) +} + +func (st *SchemaTable) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) { + if st.backingTable == nil { + return sql.RowsToRowIter(), nil + } + return st.backingTable.PartitionRows(ctx, partition) +} + +func (st *SchemaTable) LockedToRoot(ctx *sql.Context, root doltdb.RootValue) (sql.IndexAddressableTable, error) { + if st.backingTable == nil { + return st, nil + } + return st.backingTable.LockedToRoot(ctx, root) +} + +func (st *SchemaTable) IndexedAccess(lookup sql.IndexLookup) sql.IndexedTable { + // Never reached. Interface required for LockedToRoot to be implemented. + panic("Unreachable") +} + +func (st *SchemaTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { + return nil, nil +} + +func (st *SchemaTable) PreciseMatch() bool { + return true +} + +// Updater implements sql.UpdatableTable. The SchemaTable is only every modified by using it's wrapped backing table. +func (st *SchemaTable) Updater(ctx *sql.Context) sql.RowUpdater { + panic("Runtime error: SchemaTable.Updater() should never be called") +} + +func (st *SchemaTable) UnWrap() *WritableDoltTable { + return st.backingTable +} + +var _ sql.Table = (*SchemaTable)(nil) +var _ dtables.VersionableTable = (*SchemaTable)(nil) +var _ sql.IndexAddressableTable = (*SchemaTable)(nil) +var _ sql.UpdatableTable = (*SchemaTable)(nil) +var _ WritableDoltTableWrapper = (*SchemaTable)(nil) + +func SchemaTableSqlSchema() sql.PrimaryKeySchema { + sqlSchema, err := sqlutil.FromDoltSchema("", doltdb.SchemasTableName, SchemaTableSchema()) + if err != nil { + panic(err) // should never happen + } + return sqlSchema +} + func mustNewColWithTypeInfo(name string, tag uint64, typeInfo typeinfo.TypeInfo, partOfPK bool, defaultVal string, autoIncrement bool, comment string, constraints ...schema.ColConstraint) schema.Column { col, err := schema.NewColumnWithTypeInfo(name, tag, typeInfo, partOfPK, defaultVal, autoIncrement, comment, constraints...) if err != nil { @@ -56,16 +146,47 @@ func mustCreateStringType(baseType query.Type, length int64, collation sql.Colla return ti } +// dolt_schemas columns, for dolt_schemas of v1.0.0. +func SchemaTableV1SqlSchema() sql.Schema { + var schemasTableCols = schema.NewColCollection( + mustNewColWithTypeInfo(doltdb.SchemasTablesTypeCol, schema.DoltSchemasTypeTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 64, sql.Collation_utf8mb4_0900_ai_ci)), true, "", false, ""), + mustNewColWithTypeInfo(doltdb.SchemasTablesNameCol, schema.DoltSchemasNameTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 64, sql.Collation_utf8mb4_0900_ai_ci)), true, "", false, ""), + mustNewColWithTypeInfo(doltdb.SchemasTablesFragmentCol, schema.DoltSchemasFragmentTag, typeinfo.CreateVarStringTypeFromSqlType(gmstypes.LongText), false, "", false, ""), + mustNewColWithTypeInfo(doltdb.SchemasTablesExtraCol, schema.DoltSchemasExtraTag, typeinfo.JSONType, false, "", false, ""), + ) + + legacy := schema.MustSchemaFromCols(schemasTableCols) + sqlSchema, err := sqlutil.FromDoltSchema("", doltdb.SchemasTableName, legacy) + if err != nil { + panic(err) // should never happen + } + return sqlSchema.Schema +} + // dolt_schemas columns -var schemasTableCols = schema.NewColCollection( - mustNewColWithTypeInfo(doltdb.SchemasTablesTypeCol, schema.DoltSchemasTypeTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 64, sql.Collation_utf8mb4_0900_ai_ci)), true, "", false, ""), - mustNewColWithTypeInfo(doltdb.SchemasTablesNameCol, schema.DoltSchemasNameTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 64, sql.Collation_utf8mb4_0900_ai_ci)), true, "", false, ""), - mustNewColWithTypeInfo(doltdb.SchemasTablesFragmentCol, schema.DoltSchemasFragmentTag, typeinfo.CreateVarStringTypeFromSqlType(gmstypes.LongText), false, "", false, ""), - mustNewColWithTypeInfo(doltdb.SchemasTablesExtraCol, schema.DoltSchemasExtraTag, typeinfo.JSONType, false, "", false, ""), - mustNewColWithTypeInfo(doltdb.SchemasTablesSqlModeCol, schema.DoltSchemasSqlModeTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 256, sql.Collation_utf8mb4_0900_ai_ci)), false, "", false, ""), -) +func SchemaTableSchema() schema.Schema { + var schemasTableCols = schema.NewColCollection( + mustNewColWithTypeInfo(doltdb.SchemasTablesTypeCol, schema.DoltSchemasTypeTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 64, sql.Collation_utf8mb4_0900_ai_ci)), true, "", false, ""), + mustNewColWithTypeInfo(doltdb.SchemasTablesNameCol, schema.DoltSchemasNameTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 64, sql.Collation_utf8mb4_0900_ai_ci)), true, "", false, ""), + mustNewColWithTypeInfo(doltdb.SchemasTablesFragmentCol, schema.DoltSchemasFragmentTag, typeinfo.CreateVarStringTypeFromSqlType(gmstypes.LongText), false, "", false, ""), + mustNewColWithTypeInfo(doltdb.SchemasTablesExtraCol, schema.DoltSchemasExtraTag, typeinfo.JSONType, false, "", false, ""), + mustNewColWithTypeInfo(doltdb.SchemasTablesSqlModeCol, schema.DoltSchemasSqlModeTag, typeinfo.CreateVarStringTypeFromSqlType(mustCreateStringType(query.Type_VARCHAR, 256, sql.Collation_utf8mb4_0900_ai_ci)), false, "", false, ""), + ) + + return schema.MustSchemaFromCols(schemasTableCols) +} + +func NewEmptySchemaTable() sql.Table { + return &SchemaTable{} +} -var schemaTableSchema = schema.MustSchemaFromCols(schemasTableCols) +func NewSchemaTable(ctx *sql.Context, db Database, backingTable *WritableDoltTable) (sql.Table, error) { + if !backingTable.Schema().Contains(doltdb.SchemasTablesExtraCol, doltdb.SchemasTableName) { + return nil, fmt.Errorf("cannot migrate dolt_schemas table from v0.19.1 or earlier") + } + + return &SchemaTable{backingTable: backingTable}, nil +} // getOrCreateDoltSchemasTable returns the `dolt_schemas` table in `db`, creating it if it does not already exist. // Also migrates data to the correct format if necessary. @@ -88,15 +209,22 @@ func getOrCreateDoltSchemasTable(ctx *sql.Context, db Database) (retTbl *Writabl } } - tbl, found, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return nil, err } - if found { - schemasTable := tbl.(*WritableDoltTable) - // Old schemas table contains the `id` column or is missing an `extra` column. - if tbl.Schema().Contains(doltdb.SchemasTablesIdCol, doltdb.SchemasTableName) || !tbl.Schema().Contains(doltdb.SchemasTablesExtraCol, doltdb.SchemasTableName) { + wrapper, ok := tbl.(*SchemaTable) + if !ok { + return nil, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + + if wrapper.backingTable != nil { + schemasTable := wrapper.backingTable + // Old schemas are missing the `extra` column. Very ancient. Provide error message and bail. + if !schemasTable.Schema().Contains(doltdb.SchemasTablesExtraCol, doltdb.SchemasTableName) { + return nil, fmt.Errorf("cannot migrate dolt_schemas table from v0.19.1 or earlier") + } else if !schemasTable.Schema().Contains(doltdb.SchemasTablesSqlModeCol, doltdb.SchemasTableName) { return migrateOldSchemasTableToNew(ctx, db, schemasTable) } else { return schemasTable, nil @@ -104,19 +232,24 @@ func getOrCreateDoltSchemasTable(ctx *sql.Context, db Database) (retTbl *Writabl } // Create new empty table - err = db.createDoltTable(ctx, doltdb.SchemasTableName, schemaName, root, schemaTableSchema) + err = db.createDoltTable(ctx, doltdb.SchemasTableName, schemaName, root, SchemaTableSchema()) if err != nil { return nil, err } - tbl, found, err = db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err = db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return nil, err } - if !found { + + wrapper, ok = tbl.(*SchemaTable) + if !ok { + return nil, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + if wrapper.backingTable == nil { return nil, sql.ErrTableNotFound.New(doltdb.SchemasTableName) } - return tbl.(*WritableDoltTable), nil + return wrapper.backingTable, nil } func migrateOldSchemasTableToNew(ctx *sql.Context, db Database, schemasTable *WritableDoltTable) (newTable *WritableDoltTable, rerr error) { @@ -151,13 +284,12 @@ func migrateOldSchemasTableToNew(ctx *sql.Context, db Database, schemasTable *Wr return nil, err } - newRow := make(sql.Row, schemasTableCols.Size()) + newRow := make(sql.Row, SchemaTableSchema().GetAllCols().Size()) newRow[0] = sqlRow[typeIdx] newRow[1] = sqlRow[nameIdx] newRow[2] = sqlRow[fragmentIdx] - if extraIdx >= 0 { - newRow[3] = sqlRow[extraIdx] - } + newRow[3] = sqlRow[extraIdx] + if sqlModeIdx >= 0 { newRow[4] = sqlRow[sqlModeIdx] } @@ -175,20 +307,26 @@ func migrateOldSchemasTableToNew(ctx *sql.Context, db Database, schemasTable *Wr return nil, err } - err = db.createDoltTable(ctx, doltdb.SchemasTableName, doltdb.DefaultSchemaName, root, schemaTableSchema) + err = db.createDoltTable(ctx, doltdb.SchemasTableName, doltdb.DefaultSchemaName, root, SchemaTableSchema()) if err != nil { return nil, err } - tbl, found, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) if err != nil { return nil, err } - if !found { + + wrapper, ok := tbl.(*SchemaTable) + if !ok { + return nil, fmt.Errorf("expected a SchemaTable, but found %T", tbl) + } + + if wrapper.backingTable == nil { return nil, sql.ErrTableNotFound.New(doltdb.SchemasTableName) } - inserter := tbl.(*WritableDoltTable).Inserter(ctx) + inserter := wrapper.backingTable.Inserter(ctx) for _, row := range newRows { err = inserter.Insert(ctx, row) if err != nil { @@ -201,7 +339,7 @@ func migrateOldSchemasTableToNew(ctx *sql.Context, db Database, schemasTable *Wr return nil, err } - return tbl.(*WritableDoltTable), nil + return wrapper.backingTable, nil } // fragFromSchemasTable returns the row with the given schema fragment if it exists. diff --git a/go/libraries/doltcore/sqle/schema_table_test.go b/go/libraries/doltcore/sqle/schema_table_test.go index 8272429ff88..cb115bede76 100644 --- a/go/libraries/doltcore/sqle/schema_table_test.go +++ b/go/libraries/doltcore/sqle/schema_table_test.go @@ -16,13 +16,10 @@ package sqle import ( "context" - "io" - "strings" "testing" "github.com/dolthub/go-mysql-server/sql" gmstypes "github.com/dolthub/go-mysql-server/sql/types" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" @@ -30,7 +27,7 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/table/editor" ) -func TestSchemaTableMigrationOriginal(t *testing.T) { +func TestAncientSchemaTableError(t *testing.T) { dEnv := dtestutils.CreateTestEnv() tmpDir, err := dEnv.TempTableFilesDir() require.NoError(t, err) @@ -48,45 +45,13 @@ func TestSchemaTableMigrationOriginal(t *testing.T) { }), sql.Collation_Default, "") require.NoError(t, err) - sqlTbl, found, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) - require.NoError(t, err) - require.True(t, found) - - inserter := sqlTbl.(*WritableDoltTable).Inserter(ctx) - err = inserter.Insert(ctx, sql.Row{"view", "view1", "SELECT v1 FROM test;"}) - require.NoError(t, err) - err = inserter.Insert(ctx, sql.Row{"view", "view2", "SELECT v2 FROM test;"}) - require.NoError(t, err) - err = inserter.Close(ctx) - require.NoError(t, err) - - tbl, err := getOrCreateDoltSchemasTable(ctx, db) // removes the old table and recreates it with the new schema - require.NoError(t, err) - - iter, err := SqlTableToRowIter(ctx, tbl.DoltTable, nil) - require.NoError(t, err) - - var rows []sql.Row - for { - row, err := iter.Next(ctx) - if err == io.EOF { - break - } - - require.NoError(t, err) - rows = append(rows, row) - } - - require.NoError(t, iter.Close(ctx)) - expectedRows := []sql.Row{ - {"view", "view1", "SELECT v1 FROM test;", nil, nil}, - {"view", "view2", "SELECT v2 FROM test;", nil, nil}, - } + _, _, err = db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + require.Error(t, err) + require.Contains(t, err.Error(), "cannot migrate dolt_schemas table from v0.19.1 or earlier") - assert.Equal(t, expectedRows, rows) } -func TestSchemaTableMigrationV1(t *testing.T) { +func TestV1SchemasTable(t *testing.T) { dEnv := dtestutils.CreateTestEnv() tmpDir, err := dEnv.TempTableFilesDir() require.NoError(t, err) @@ -97,63 +62,38 @@ func TestSchemaTableMigrationV1(t *testing.T) { _, ctx, err := NewTestEngine(dEnv, context.Background(), db) require.NoError(t, err) - // original schema of dolt_schemas table with the ID column - err = db.createSqlTable(ctx, doltdb.SchemasTableName, "", sql.NewPrimaryKeySchema(sql.Schema{ - {Name: doltdb.SchemasTablesTypeCol, Type: gmstypes.Text, Source: doltdb.SchemasTableName, PrimaryKey: false}, - {Name: doltdb.SchemasTablesNameCol, Type: gmstypes.Text, Source: doltdb.SchemasTableName, PrimaryKey: false}, + err = db.createSqlTable(ctx, doltdb.SchemasTableName, "", sql.NewPrimaryKeySchema(sql.Schema{ // original schema of dolt_schemas table + {Name: doltdb.SchemasTablesTypeCol, Type: gmstypes.Text, Source: doltdb.SchemasTableName, PrimaryKey: true}, + {Name: doltdb.SchemasTablesNameCol, Type: gmstypes.Text, Source: doltdb.SchemasTableName, PrimaryKey: true}, {Name: doltdb.SchemasTablesFragmentCol, Type: gmstypes.Text, Source: doltdb.SchemasTableName, PrimaryKey: false}, - {Name: doltdb.SchemasTablesIdCol, Type: gmstypes.Int64, Source: doltdb.SchemasTableName, PrimaryKey: true}, - {Name: doltdb.SchemasTablesExtraCol, Type: gmstypes.JsonType{}, Source: doltdb.SchemasTableName, PrimaryKey: false, Nullable: true}, + {Name: doltdb.SchemasTablesExtraCol, Type: gmstypes.JSON, Source: doltdb.SchemasTableName, PrimaryKey: false}, }), sql.Collation_Default, "") require.NoError(t, err) - sqlTbl, found, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + tbl, _, err := db.GetTableInsensitive(ctx, doltdb.SchemasTableName) require.NoError(t, err) - require.True(t, found) - inserter := sqlTbl.(*WritableDoltTable).Inserter(ctx) - // JSON string has no spaces because our various JSON libraries don't agree on how to marshall it - err = inserter.Insert(ctx, sql.Row{"view", "view1", "SELECT v1 FROM test;", 1, `{"extra":"data"}`}) - require.NoError(t, err) - err = inserter.Insert(ctx, sql.Row{"view", "view2", "SELECT v2 FROM test;", 2, nil}) - require.NoError(t, err) - err = inserter.Close(ctx) - require.NoError(t, err) + wrapper, ok := tbl.(*SchemaTable) + require.True(t, ok) + require.NotNil(t, wrapper.backingTable) - tbl, err := getOrCreateDoltSchemasTable(ctx, db) // removes the old table and recreates it with the new schema - require.NoError(t, err) + // unmodified dolt_schemas table. + require.Equal(t, 4, len(wrapper.backingTable.Schema())) - iter, err := SqlTableToRowIter(ctx, tbl.DoltTable, nil) + tbl, err = getOrCreateDoltSchemasTable(ctx, db) require.NoError(t, err) + require.NotNil(t, tbl) - var rows []sql.Row - for { - row, err := iter.Next(ctx) - if err == io.EOF { - break - } - - require.NoError(t, err) - // convert the JSONDocument to a string for comparison - if row[3] != nil { - jsonDoc, ok := row[3].(sql.JSONWrapper) - if ok { - row[3], err = gmstypes.StringifyJSON(jsonDoc) - row[3] = strings.ReplaceAll(row[3].(string), " ", "") // remove spaces - } - - require.NoError(t, err) - } + // modified dolt_schemas table. + require.Equal(t, 5, len(tbl.Schema())) - rows = append(rows, row) - } - - require.NoError(t, iter.Close(ctx)) + tbl, _, err = db.GetTableInsensitive(ctx, doltdb.SchemasTableName) + require.NoError(t, err) + wrapper, ok = tbl.(*SchemaTable) + require.True(t, ok) + require.NotNil(t, wrapper.backingTable) - expectedRows := []sql.Row{ - {"view", "view1", "SELECT v1 FROM test;", `{"extra":"data"}`, nil}, - {"view", "view2", "SELECT v2 FROM test;", nil, nil}, - } + // modified dolt_schemas table. + require.Equal(t, 5, len(wrapper.backingTable.Schema())) - assert.Equal(t, expectedRows, rows) } diff --git a/go/libraries/doltcore/sqle/sqlddl_test.go b/go/libraries/doltcore/sqle/sqlddl_test.go index 5a9b5115c17..2fd3d6acd85 100644 --- a/go/libraries/doltcore/sqle/sqlddl_test.go +++ b/go/libraries/doltcore/sqle/sqlddl_test.go @@ -819,9 +819,8 @@ func TestAlterSystemTables(t *testing.T) { "INSERT INTO dolt_docs VALUES ('LICENSE.md','A license')") CreateTestTable(t, dEnv, doltdb.DoltQueryCatalogTableName, dtables.DoltQueryCatalogSchema, "INSERT INTO dolt_query_catalog VALUES ('abc123', 1, 'example', 'select 2+2 from dual', 'description')") - CreateTestTable(t, dEnv, doltdb.SchemasTableName, schemaTableSchema, - "INSERT INTO dolt_schemas (type, name, fragment) VALUES ('view', 'name', 'create view name as select 2+2 from dual')") ExecuteSetupSQL(context.Background(), ` + CREATE VIEW name as select 2+2 from dual; CREATE PROCEDURE simple_proc2() SELECT 1+1; INSERT INTO dolt_ignore VALUES ('test', 1);`)(t, dEnv) } diff --git a/go/libraries/doltcore/sqle/sqldelete_test.go b/go/libraries/doltcore/sqle/sqldelete_test.go index 9ca25c97bed..b5e62b04359 100644 --- a/go/libraries/doltcore/sqle/sqldelete_test.go +++ b/go/libraries/doltcore/sqle/sqldelete_test.go @@ -151,22 +151,22 @@ var BasicDeleteTests = []DeleteTest{ { Name: "delete invalid table", DeleteQuery: "delete from nobody", - ExpectedErr: "invalid table", + ExpectedErr: "table not found: nobody", }, { Name: "delete invalid column", DeleteQuery: "delete from people where z = 'dne'", - ExpectedErr: "invalid column", + ExpectedErr: "column \"z\" could not be found in any table in scope", }, { Name: "delete negative limit", DeleteQuery: "delete from people limit -1", - ExpectedErr: "invalid limit number", + ExpectedErr: "syntax error", // syntax error at position 27 near 'limit' }, { Name: "delete negative offset", DeleteQuery: "delete from people limit 1 offset -1", - ExpectedErr: "invalid limit number", + ExpectedErr: "syntax error", // syntax error at position 36 near 'offset' }, } @@ -207,12 +207,10 @@ var systemTableDeleteTests = []DeleteTest{ }, { Name: "delete dolt_schemas", - AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, schemaTableSchema, - "INSERT INTO dolt_schemas (type, name, fragment) VALUES ('view', 'name', 'create view name as select 2+2 from dual')"), - DeleteQuery: "delete from dolt_schemas", - SelectQuery: "select * from dolt_schemas", - ExpectedRows: ToSqlRows(dtables.DoltQueryCatalogSchema), - ExpectedSchema: schemaTableSchema, + AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, SchemaTableSchema(), + "CREATE VIEW name as select 2+2 from dual"), + DeleteQuery: "delete from dolt_schemas", + ExpectedErr: "table doesn't support DELETE FROM", }, } @@ -239,6 +237,7 @@ func testDeleteQuery(t *testing.T, test DeleteTest) { root, err = executeModify(t, context.Background(), dEnv, root, test.DeleteQuery) if len(test.ExpectedErr) > 0 { require.Error(t, err) + assert.Contains(t, err.Error(), test.ExpectedErr) return } else { require.NoError(t, err) diff --git a/go/libraries/doltcore/sqle/sqlinsert_test.go b/go/libraries/doltcore/sqle/sqlinsert_test.go index 2dc980a5ae9..e734516bfc8 100644 --- a/go/libraries/doltcore/sqle/sqlinsert_test.go +++ b/go/libraries/doltcore/sqle/sqlinsert_test.go @@ -68,12 +68,12 @@ var BasicInsertTests = []InsertTest{ { Name: "insert no columns too few values", InsertQuery: "insert into people values (2, 'Bart', 'Simpson', false, 10, 9, '00000000-0000-0000-0000-000000000002')", - ExpectedErr: "too few values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "insert no columns too many values", InsertQuery: "insert into people values (2, 'Bart', 'Simpson', false, 10, 9, '00000000-0000-0000-0000-000000000002', 222, 'abc')", - ExpectedErr: "too many values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "insert full columns", @@ -127,27 +127,27 @@ var BasicInsertTests = []InsertTest{ { Name: "insert partial columns duplicate column", InsertQuery: "insert into people (id, first_name, last_name, first_name) values (2, 'Bart', 'Simpson', 'Bart')", - ExpectedErr: "duplicate column", + ExpectedErr: "column 'first_name' specified twice", }, { Name: "insert partial columns invalid column", InsertQuery: "insert into people (id, first_name, last_name, middle) values (2, 'Bart', 'Simpson', 'Nani')", - ExpectedErr: "duplicate column", + ExpectedErr: "Unknown column 'middle' in 'people'", }, { Name: "insert missing non-nullable column", InsertQuery: "insert into people (id, first_name) values (2, 'Bart')", - ExpectedErr: "column received nil but is non-nullable", + ExpectedErr: "Field 'last_name' doesn't have a default value", }, { Name: "insert partial columns mismatch too many values", InsertQuery: "insert into people (id, first_name, last_name) values (2, 'Bart', 'Simpson', false)", - ExpectedErr: "too many values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "insert partial columns mismatch too few values", InsertQuery: "insert into people (id, first_name, last_name) values (2, 'Bart')", - ExpectedErr: "too few values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "insert partial columns functions", @@ -228,7 +228,7 @@ var BasicInsertTests = []InsertTest{ { Name: "insert partial columns multiple rows null pk", InsertQuery: "insert into people (id, first_name, last_name) values (0, 'Bart', 'Simpson'), (1, 'Homer', null)", - ExpectedErr: "column received nil but is non-nullable", + ExpectedErr: "column name 'last_name' is non-nullable but attempted to set a value of null", }, { Name: "insert partial columns multiple rows duplicate", @@ -397,13 +397,9 @@ var systemTableInsertTests = []InsertTest{ }, { Name: "insert into dolt_schemas", - AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, schemaTableSchema, ""), + AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, SchemaTableSchema(), ""), InsertQuery: "insert into dolt_schemas (type, name, fragment) values ('view', 'name', 'create view name as select 2+2 from dual')", - SelectQuery: "select * from dolt_schemas ORDER BY name", - ExpectedRows: ToSqlRows(CompressSchema(schemaTableSchema), - NewRow(types.String("view"), types.String("name"), types.String("create view name as select 2+2 from dual")), - ), - ExpectedSchema: CompressSchema(schemaTableSchema), + ExpectedErr: "table doesn't support INSERT INTO", }, } @@ -442,6 +438,7 @@ func testInsertQuery(t *testing.T, test InsertTest) { root, err = executeModify(t, context.Background(), dEnv, root, test.InsertQuery) if len(test.ExpectedErr) > 0 { require.Error(t, err) + assert.Contains(t, err.Error(), test.ExpectedErr) return } else { require.NoError(t, err) diff --git a/go/libraries/doltcore/sqle/sqlreplace_test.go b/go/libraries/doltcore/sqle/sqlreplace_test.go index a5469af2f5c..790abd85ff3 100644 --- a/go/libraries/doltcore/sqle/sqlreplace_test.go +++ b/go/libraries/doltcore/sqle/sqlreplace_test.go @@ -70,12 +70,12 @@ var BasicReplaceTests = []ReplaceTest{ { Name: "replace no columns too few values", ReplaceQuery: "replace into people values (2, 'Bart', 'Simpson', false, 10, 9, '00000000-0000-0000-0000-000000000002')", - ExpectedErr: "too few values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "replace no columns too many values", ReplaceQuery: "replace into people values (2, 'Bart', 'Simpson', false, 10, 9, '00000000-0000-0000-0000-000000000002', 222, 'abc')", - ExpectedErr: "too many values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "replace full columns", @@ -129,27 +129,27 @@ var BasicReplaceTests = []ReplaceTest{ { Name: "replace partial columns duplicate column", ReplaceQuery: "replace into people (id, first_name, last_name, first_name) values (2, 'Bart', 'Simpson', 'Bart')", - ExpectedErr: "duplicate column", + ExpectedErr: "column 'first_name' specified twice", }, { Name: "replace partial columns invalid column", ReplaceQuery: "replace into people (id, first_name, last_name, middle) values (2, 'Bart', 'Simpson', 'Nani')", - ExpectedErr: "duplicate column", + ExpectedErr: "Unknown column 'middle' in 'people'", }, { Name: "replace missing non-nullable column", ReplaceQuery: "replace into people (id, first_name) values (2, 'Bart')", - ExpectedErr: "column received nil but is non-nullable", + ExpectedErr: "Field 'last_name' doesn't have a default value", }, { Name: "replace partial columns mismatch too many values", ReplaceQuery: "replace into people (id, first_name, last_name) values (2, 'Bart', 'Simpson', false)", - ExpectedErr: "too many values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "replace partial columns mismatch too few values", ReplaceQuery: "replace into people (id, first_name, last_name) values (2, 'Bart')", - ExpectedErr: "too few values", + ExpectedErr: "number of values does not match number of columns provided", }, { Name: "replace partial columns functions", @@ -194,7 +194,7 @@ var BasicReplaceTests = []ReplaceTest{ { Name: "replace partial columns multiple rows null pk", ReplaceQuery: "replace into people (id, first_name, last_name) values (0, 'Bart', 'Simpson'), (1, 'Homer', null)", - ExpectedErr: "column received nil but is non-nullable", + ExpectedErr: "column name 'last_name' is non-nullable but attempted to set a value of null", }, { Name: "replace partial columns multiple rows duplicate", @@ -225,7 +225,7 @@ var BasicReplaceTests = []ReplaceTest{ (0, "Homer", "Simpson", true, 45, 100), (8, "Milhouse", "Van Houten", false, 8, 3.5), (7, "Maggie", null, false, 1, 5.1)`, - ExpectedErr: "Constraint failed for column 'last_name': Not null", + ExpectedErr: "column name 'last_name' is non-nullable but attempted to set a value of null", }, { Name: "replace partial columns existing pk", @@ -272,12 +272,10 @@ var systemTableReplaceTests = []ReplaceTest{ }, { Name: "replace into dolt_schemas", - AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, schemaTableSchema, - "INSERT INTO dolt_schemas VALUES ('view', 'name', 'create view name as select 2+2 from dual', NULL, NULL)"), - ReplaceQuery: "replace into dolt_schemas (type, name, fragment) values ('view', 'name', 'create view name as select 1+1 from dual')", - SelectQuery: "select type, name, fragment, extra, sql_mode from dolt_schemas", - ExpectedRows: []sql.Row{{"view", "name", "create view name as select 1+1 from dual", nil, nil}}, - ExpectedSchema: CompressSchema(schemaTableSchema), + AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, SchemaTableSchema(), + "CREATE VIEW name as select 2+2 from dual"), + ReplaceQuery: "replace into dolt_schemas (type, name, fragment) values ('view', 'name', 'create view name as select 1+1 from dual')", + ExpectedErr: "table doesn't support INSERT INTO", }, } @@ -312,6 +310,7 @@ func testReplaceQuery(t *testing.T, test ReplaceTest) { root, err = executeModify(t, context.Background(), dEnv, root, test.ReplaceQuery) if len(test.ExpectedErr) > 0 { require.Error(t, err) + assert.Contains(t, err.Error(), test.ExpectedErr) return } else { require.NoError(t, err) diff --git a/go/libraries/doltcore/sqle/sqlselect_test.go b/go/libraries/doltcore/sqle/sqlselect_test.go index 3c7bd541a9f..7f8fc08464d 100644 --- a/go/libraries/doltcore/sqle/sqlselect_test.go +++ b/go/libraries/doltcore/sqle/sqlselect_test.go @@ -33,7 +33,6 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/schema" "github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" - "github.com/dolthub/dolt/go/libraries/doltcore/sqle/json" "github.com/dolthub/dolt/go/store/datas" "github.com/dolthub/dolt/go/store/types" ) @@ -1308,11 +1307,11 @@ var systemTableSelectTests = []SelectTest{ }, { Name: "select from dolt_schemas", - AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, schemaTableSchema, - `INSERT INTO dolt_schemas VALUES ('view', 'name', 'create view name as select 2+2 from dual', NULL, NULL)`), + AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, SchemaTableSchema(), + `CREATE VIEW name as select 2+2 from dual`), Query: "select * from dolt_schemas", - ExpectedRows: []sql.Row{{"view", "name", "create view name as select 2+2 from dual", nil, nil}}, - ExpectedSchema: CompressSchema(schemaTableSchema), + ExpectedRows: []sql.Row{{"view", "name", "CREATE VIEW name as select 2+2 from dual", ignoreVal, "NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES"}}, + ExpectedSchema: CompressSchema(SchemaTableSchema()), }, } @@ -1346,6 +1345,10 @@ func installTestCommitClock() func() { } } +type testIgnoredValue struct{} + +var ignoreVal = testIgnoredValue{} + // Tests the given query on a freshly created dataset, asserting that the result has the given schema and rows. If // expectedErr is set, asserts instead that the execution returns an error that matches. func testSelectQuery(t *testing.T, test SelectTest) { @@ -1378,14 +1381,10 @@ func testSelectQuery(t *testing.T, test SelectTest) { for i := 0; i < len(test.ExpectedRows); i++ { assert.Equal(t, len(test.ExpectedRows[i]), len(actualRows[i])) for j := 0; j < len(test.ExpectedRows[i]); j++ { - if _, ok := actualRows[i][j].(json.NomsJSON); ok { - cmp, err := gmstypes.CompareJSON(actualRows[i][j].(json.NomsJSON), test.ExpectedRows[i][j].(json.NomsJSON)) - assert.NoError(t, err) - assert.Equal(t, 0, cmp) - } else { - assert.Equal(t, test.ExpectedRows[i][j], actualRows[i][j]) + if test.ExpectedRows[i][j] == ignoreVal { + continue } - + assert.Equal(t, test.ExpectedRows[i][j], actualRows[i][j]) } } diff --git a/go/libraries/doltcore/sqle/sqlupdate_test.go b/go/libraries/doltcore/sqle/sqlupdate_test.go index 8d92735b6e0..2ca58d78858 100644 --- a/go/libraries/doltcore/sqle/sqlupdate_test.go +++ b/go/libraries/doltcore/sqle/sqlupdate_test.go @@ -376,15 +376,6 @@ var systemTableUpdateTests = []UpdateTest{ ExpectedRows: []sql.Row{{"abc123", uint64(2), "example", "select 2+2 from dual", "description"}}, ExpectedSchema: CompressSchema(dtables.DoltQueryCatalogSchema), }, - { - Name: "update dolt_schemas", - AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, schemaTableSchema, - `INSERT INTO dolt_schemas VALUES ('view', 'name', 'create view name as select 2+2 from dual', NULL, NULL)`), - UpdateQuery: "update dolt_schemas set type = 'not a view'", - SelectQuery: "select * from dolt_schemas", - ExpectedRows: []sql.Row{{"not a view", "name", "create view name as select 2+2 from dual", nil, nil}}, - ExpectedSchema: CompressSchema(schemaTableSchema), - }, } // Tests the given query on a freshly created dataset, asserting that the result has the given schema and rows. If diff --git a/go/libraries/doltcore/sqle/tables.go b/go/libraries/doltcore/sqle/tables.go index d56e0149fbd..6596a815910 100644 --- a/go/libraries/doltcore/sqle/tables.go +++ b/go/libraries/doltcore/sqle/tables.go @@ -536,6 +536,14 @@ type WritableDoltTable struct { var _ doltTableInterface = (*WritableDoltTable)(nil) +// WritableDoltTableWrapper is an interface that allows a table to be returned as an sql.Table, but actually be a wrapped +// fake table. Specifically, databases.getTableInsensitive will returns an sql.Table, and there are cases where we +// want to return a table that hasn't been materialized yet. +type WritableDoltTableWrapper interface { + // Unwrap returns the underlying WritableDoltTable, nil returns are expected when the wrapped table hasn't been materialized + UnWrap() *WritableDoltTable +} + // Internal interface for declaring the interfaces that writable dolt tables are expected to implement type doltTableInterface interface { sql.UpdatableTable diff --git a/integration-tests/bats/helper/old_dolt_schemas/config.json b/integration-tests/bats/helper/old_dolt_schemas/config.json deleted file mode 100644 index 9e26dfeeb6e..00000000000 --- a/integration-tests/bats/helper/old_dolt_schemas/config.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/4bujnjuatdli0klda7ucot0tl836jujt b/integration-tests/bats/helper/old_dolt_schemas/noms/4bujnjuatdli0klda7ucot0tl836jujt deleted file mode 100644 index c41b419f7c2..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/4bujnjuatdli0klda7ucot0tl836jujt and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/5fms6l3ffldbk6t8qjumnjstmurvppk8 b/integration-tests/bats/helper/old_dolt_schemas/noms/5fms6l3ffldbk6t8qjumnjstmurvppk8 new file mode 100644 index 00000000000..fd7fa87f96e Binary files /dev/null and b/integration-tests/bats/helper/old_dolt_schemas/noms/5fms6l3ffldbk6t8qjumnjstmurvppk8 differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/63c0dojobm5j7arp99hiq78hj1rtp7m5 b/integration-tests/bats/helper/old_dolt_schemas/noms/63c0dojobm5j7arp99hiq78hj1rtp7m5 deleted file mode 100644 index c9381672d3a..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/63c0dojobm5j7arp99hiq78hj1rtp7m5 and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/aum9p7826iqaeq0pjtqlcn56hoj5nhht b/integration-tests/bats/helper/old_dolt_schemas/noms/aum9p7826iqaeq0pjtqlcn56hoj5nhht deleted file mode 100644 index 3531c8a1e25..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/aum9p7826iqaeq0pjtqlcn56hoj5nhht and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/chghnlvugtiui6khfo29cpfnfr3166qc b/integration-tests/bats/helper/old_dolt_schemas/noms/chghnlvugtiui6khfo29cpfnfr3166qc deleted file mode 100644 index db6bee87089..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/chghnlvugtiui6khfo29cpfnfr3166qc and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/d0b8ibrrjvvv3f0mtd7hathtjq2vhig9 b/integration-tests/bats/helper/old_dolt_schemas/noms/d0b8ibrrjvvv3f0mtd7hathtjq2vhig9 deleted file mode 100644 index ad2f1859d31..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/d0b8ibrrjvvv3f0mtd7hathtjq2vhig9 and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/erturgf7to0f6r8prlbllflbshg5li4n b/integration-tests/bats/helper/old_dolt_schemas/noms/erturgf7to0f6r8prlbllflbshg5li4n deleted file mode 100644 index 4c4f95b67e6..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/erturgf7to0f6r8prlbllflbshg5li4n and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/manifest b/integration-tests/bats/helper/old_dolt_schemas/noms/manifest index 1641eab0ce4..82cba56d0e9 100644 --- a/integration-tests/bats/helper/old_dolt_schemas/noms/manifest +++ b/integration-tests/bats/helper/old_dolt_schemas/noms/manifest @@ -1 +1 @@ -4:__LD_1__:ma67ier4cau1ai1jhk2fom2m3bggmfa3:iht2tkfo9cbbnrfmla9ks4gahc0snq4o:63c0dojobm5j7arp99hiq78hj1rtp7m5:2:ro000svvgga0k7hatg7fta9j8ismtc3b:1:chghnlvugtiui6khfo29cpfnfr3166qc:1:d0b8ibrrjvvv3f0mtd7hathtjq2vhig9:1:oktjimrpqi8m9dbpt3c7ikoaolhgtr2n:6:oik6v50p7q6as4t7qg7f1i06iomt9vn9:2:4bujnjuatdli0klda7ucot0tl836jujt:1:qe0mct6rf8puhdnqecm185p63dacaqrt:4:erturgf7to0f6r8prlbllflbshg5li4n:1:u9c6ttq6ddrgqdfbod9pg2vv79ab6crt:2:aum9p7826iqaeq0pjtqlcn56hoj5nhht:2 \ No newline at end of file +5:__DOLT__:mmm45bv7kr0i1hiljdsejn14gupjpe26:19570u4rtu23963flgcvml2a85gfos1a:mmm45bv7kr0i1hiljdsejn14gupjpe26:5fms6l3ffldbk6t8qjumnjstmurvppk8:2 \ No newline at end of file diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/oik6v50p7q6as4t7qg7f1i06iomt9vn9 b/integration-tests/bats/helper/old_dolt_schemas/noms/oik6v50p7q6as4t7qg7f1i06iomt9vn9 deleted file mode 100644 index acdfd6744ed..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/oik6v50p7q6as4t7qg7f1i06iomt9vn9 and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/oktjimrpqi8m9dbpt3c7ikoaolhgtr2n b/integration-tests/bats/helper/old_dolt_schemas/noms/oktjimrpqi8m9dbpt3c7ikoaolhgtr2n deleted file mode 100644 index 22836789037..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/oktjimrpqi8m9dbpt3c7ikoaolhgtr2n and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/6vvlbeph7glt2sskq43npe56la8p9akc b/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/6vvlbeph7glt2sskq43npe56la8p9akc new file mode 100644 index 00000000000..386de8771b4 Binary files /dev/null and b/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/6vvlbeph7glt2sskq43npe56la8p9akc differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/LOCK b/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/LOCK new file mode 100644 index 00000000000..e69de29bb2d diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/manifest b/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/manifest new file mode 100644 index 00000000000..d36d7f961a5 --- /dev/null +++ b/integration-tests/bats/helper/old_dolt_schemas/noms/oldgen/manifest @@ -0,0 +1 @@ +5:__DOLT__:9trs9rc9jmja9h13v3bqrf1k8i8pvjie:00000000000000000000000000000000:00000000000000000000000000000000:6vvlbeph7glt2sskq43npe56la8p9akc:9 \ No newline at end of file diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/qe0mct6rf8puhdnqecm185p63dacaqrt b/integration-tests/bats/helper/old_dolt_schemas/noms/qe0mct6rf8puhdnqecm185p63dacaqrt deleted file mode 100644 index 1ebdc102959..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/qe0mct6rf8puhdnqecm185p63dacaqrt and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/ro000svvgga0k7hatg7fta9j8ismtc3b b/integration-tests/bats/helper/old_dolt_schemas/noms/ro000svvgga0k7hatg7fta9j8ismtc3b deleted file mode 100644 index 27512277115..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/ro000svvgga0k7hatg7fta9j8ismtc3b and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/noms/u9c6ttq6ddrgqdfbod9pg2vv79ab6crt b/integration-tests/bats/helper/old_dolt_schemas/noms/u9c6ttq6ddrgqdfbod9pg2vv79ab6crt deleted file mode 100644 index fd96fa9a507..00000000000 Binary files a/integration-tests/bats/helper/old_dolt_schemas/noms/u9c6ttq6ddrgqdfbod9pg2vv79ab6crt and /dev/null differ diff --git a/integration-tests/bats/helper/old_dolt_schemas/repo_state.json b/integration-tests/bats/helper/old_dolt_schemas/repo_state.json old mode 100644 new mode 100755 index 3eb93986fad..32f2d6197d5 --- a/integration-tests/bats/helper/old_dolt_schemas/repo_state.json +++ b/integration-tests/bats/helper/old_dolt_schemas/repo_state.json @@ -1,8 +1,6 @@ { - "head": "refs/heads/master", - "staged": "s0o665ofbugmrfechrp7vq1390oefojf", - "working": "s0o665ofbugmrfechrp7vq1390oefojf", - "merge": null, + "head": "refs/heads/main", "remotes": {}, + "backups": {}, "branches": {} } \ No newline at end of file diff --git a/integration-tests/bats/triggers.bats b/integration-tests/bats/triggers.bats index e337706f130..79b2d998f25 100644 --- a/integration-tests/bats/triggers.bats +++ b/integration-tests/bats/triggers.bats @@ -119,16 +119,12 @@ SQL [[ "$output" =~ "trigger1,INSERT,test,1,,SET new.v1 = new.v1 + 1,BEFORE,root@localhost,utf8mb4,utf8mb4_0900_bin,utf8mb4_0900_bin" ]] || false } -@test "triggers: Writing directly into dolt_schemas" { +@test "triggers: Writing directly into dolt_schemas is forbidden" { dolt sql -q "CREATE TABLE test(pk BIGINT PRIMARY KEY, v1 BIGINT);" dolt sql -q "CREATE VIEW view1 AS SELECT v1 FROM test;" - dolt sql -q "INSERT INTO dolt_schemas VALUES ('trigger', 'trigger1', 'CREATE TRIGGER trigger1 BEFORE INSERT ON test FOR EACH ROW SET new.v1 = -new.v1;', json_object('CreatedAt', 1), NULL);" - dolt sql -q "INSERT INTO test VALUES (1, 1);" - run dolt sql -q "SELECT * FROM test" -r=csv - [ "$status" -eq "0" ] - [[ "$output" =~ "pk,v1" ]] || false - [[ "$output" =~ "1,-1" ]] || false - [[ "${#lines[@]}" = "2" ]] || false + run dolt sql -q "INSERT INTO dolt_schemas VALUES ('trigger', 'trigger1', 'CREATE TRIGGER trigger1 BEFORE INSERT ON test FOR EACH ROW SET new.v1 = -new.v1;', json_object('CreatedAt', 1), NULL);" + [ "$status" -eq 1 ] + [[ "$output" =~ "doesn't support INSERT INTO" ]] || false } @test "triggers: Merge triggers on different branches, no conflict" { @@ -172,46 +168,19 @@ SQL @test "triggers: Upgrade dolt_schemas" { rm -rf .dolt - # old_dolt_schemas was created using v0.19.1, which is pre-id change + + # old_dolt_schemas was created using v1.0.0, which is pre-sqlMode change cp -a $BATS_TEST_DIRNAME/helper/old_dolt_schemas/. ./.dolt/ run dolt sql -q "SELECT * FROM dolt_schemas" -r=csv [ "$status" -eq "0" ] - [[ "$output" =~ "type,name,fragment" ]] || false - [[ "$output" =~ "view,view1,SELECT 2+2 FROM dual" ]] || false - [[ "${#lines[@]}" = "2" ]] || false - - run dolt sql -q "SELECT * FROM view1" -r=csv - [ "$status" -eq "0" ] - [[ "$output" =~ "2+2" ]] || false - [[ "$output" =~ "4" ]] || false - [[ "${#lines[@]}" = "2" ]] || false - - # creating a new view/trigger will recreate the dolt_schemas table - dolt sql -q "CREATE VIEW view2 AS SELECT 3+3 FROM dual;" + [[ "$output" =~ "type,name,fragment,extra" ]] || false + [[ "$output" =~ "view,my_view" ]] || false + [[ "$output" =~ "SELECT 2+2" ]] || false - skip "diff is broken on schema change" - run dolt diff - [ "$status" -eq "0" ] - [[ "$output" =~ "deleted table" ]] || false - [[ "$output" =~ "added table" ]] || false + dolt sql -q "CREATE VIEW another_view AS SELECT 3+3" run dolt sql -q "SELECT * FROM dolt_schemas" -r=csv [ "$status" -eq "0" ] - [[ "$output" =~ "type,name,fragment,id" ]] || false - [[ "$output" =~ "view,view1,CREATE VIEW view1 AS SELECT 2+2 FROM dual,1" ]] || false - [[ "$output" =~ "view,view2,CREATE VIEW view2 AS SELECT 3+3 FROM dual,2" ]] || false - [[ "${#lines[@]}" = "3" ]] || false - - run dolt sql -q "SELECT * FROM view1" -r=csv - [ "$status" -eq "0" ] - [[ "$output" =~ "2+2" ]] || false - [[ "$output" =~ "4" ]] || false - [[ "${#lines[@]}" = "2" ]] || false - - run dolt sql -q "SELECT * FROM view2" -r=csv - [ "$status" -eq "0" ] - [[ "$output" =~ "3+3" ]] || false - [[ "$output" =~ "6" ]] || false - [[ "${#lines[@]}" = "2" ]] || false + [[ "$output" =~ "type,name,fragment,extra,sql_mode" ]] || false } diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/views.js b/integration-tests/mysql-client-tests/node/workbenchTests/views.js index 31fbad9c424..965a804ab03 100644 --- a/integration-tests/mysql-client-tests/node/workbenchTests/views.js +++ b/integration-tests/mysql-client-tests/node/workbenchTests/views.js @@ -11,7 +11,7 @@ export const viewsTests = [ { q: "SELECT * FROM ::tableName ::col0 LIMIT :limit OFFSET :offset", p: { tableName: "dolt_schemas", col0: "id", limit: 10, offset: 0 }, - expectedErr: "table not found: dolt_schemas", + res: [], }, { q: "CREATE VIEW ::name AS SELECT * FROM test",