github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/schema_table.go (about) 1 // Copyright 2020 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sqle 16 17 import ( 18 "fmt" 19 "io" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 23 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 24 "github.com/dolthub/dolt/go/libraries/doltcore/row" 25 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 26 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 27 "github.com/dolthub/dolt/go/store/types" 28 ) 29 30 // The fixed SQL schema for the `dolt_schemas` table. 31 func SchemasTableSqlSchema() sql.Schema { 32 sqlSchema, err := sqlutil.FromDoltSchema(doltdb.SchemasTableName, SchemasTableSchema()) 33 if err != nil { 34 panic(err) // should never happen 35 } 36 return sqlSchema 37 } 38 39 // The fixed dolt schema for the `dolt_schemas` table. 40 func SchemasTableSchema() schema.Schema { 41 colColl := schema.NewColCollection( 42 schema.NewColumn(doltdb.SchemasTablesTypeCol, schema.DoltSchemasTypeTag, types.StringKind, false), 43 schema.NewColumn(doltdb.SchemasTablesNameCol, schema.DoltSchemasNameTag, types.StringKind, false), 44 schema.NewColumn(doltdb.SchemasTablesFragmentCol, schema.DoltSchemasFragmentTag, types.StringKind, false), 45 schema.NewColumn(doltdb.SchemasTablesIdCol, schema.DoltSchemasIdTag, types.IntKind, true, schema.NotNullConstraint{}), 46 ) 47 return schema.MustSchemaFromCols(colColl) 48 } 49 50 // GetOrCreateDoltSchemasTable returns the `dolt_schemas` table in `db`, creating it if it does not already exist. 51 func GetOrCreateDoltSchemasTable(ctx *sql.Context, db Database) (retTbl *WritableDoltTable, retErr error) { 52 root, err := db.GetRoot(ctx) 53 if err != nil { 54 return nil, err 55 } 56 tbl, found, err := db.GetTableInsensitiveWithRoot(ctx, root, doltdb.SchemasTableName) 57 if err != nil { 58 return nil, err 59 } 60 var rowsToAdd []sql.Row 61 if found { 62 schemasTable := tbl.(*WritableDoltTable) 63 // Old schemas table does not contain the `id` column. 64 if !tbl.Schema().Contains(doltdb.SchemasTablesIdCol, doltdb.SchemasTableName) { 65 root, rowsToAdd, err = migrateOldSchemasTableToNew(ctx, db, root, schemasTable) 66 if err != nil { 67 return nil, err 68 } 69 } else { 70 return schemasTable, nil 71 } 72 } 73 // Create the schemas table as an empty table 74 err = db.createDoltTable(ctx, doltdb.SchemasTableName, root, SchemasTableSchema()) 75 if err != nil { 76 return nil, err 77 } 78 root, err = db.GetRoot(ctx) 79 if err != nil { 80 return nil, err 81 } 82 tbl, found, err = db.GetTableInsensitiveWithRoot(ctx, root, doltdb.SchemasTableName) 83 if err != nil { 84 return nil, err 85 } 86 if !found { 87 return nil, sql.ErrTableNotFound.New("dolt_schemas") 88 } 89 // Create a unique index on the old primary key columns (type, name) 90 err = (&AlterableDoltTable{*tbl.(*WritableDoltTable)}).CreateIndex(ctx, 91 doltdb.SchemasTablesIndexName, 92 sql.IndexUsing_Default, 93 sql.IndexConstraint_Unique, 94 []sql.IndexColumn{ 95 {Name: doltdb.SchemasTablesTypeCol, Length: 0}, 96 {Name: doltdb.SchemasTablesNameCol, Length: 0}, 97 }, 98 "", 99 ) 100 if err != nil { 101 return nil, err 102 } 103 // If there was an old schemas table that contained rows, then add that data here 104 root, err = db.GetRoot(ctx) 105 if err != nil { 106 return nil, err 107 } 108 tbl, found, err = db.GetTableInsensitiveWithRoot(ctx, root, doltdb.SchemasTableName) 109 if err != nil { 110 return nil, err 111 } 112 if !found { 113 return nil, sql.ErrTableNotFound.New("dolt_schemas") 114 } 115 if len(rowsToAdd) != 0 { 116 err = func() (retErr error) { 117 inserter := tbl.(*WritableDoltTable).Inserter(ctx) 118 defer func() { 119 err := inserter.Close(ctx) 120 if retErr == nil { 121 retErr = err 122 } 123 }() 124 for _, sqlRow := range rowsToAdd { 125 err = inserter.Insert(ctx, sqlRow) 126 if err != nil { 127 return err 128 } 129 } 130 return nil 131 }() 132 if err != nil { 133 return nil, err 134 } 135 } 136 return tbl.(*WritableDoltTable), nil 137 } 138 139 func migrateOldSchemasTableToNew( 140 ctx *sql.Context, 141 db Database, 142 root *doltdb.RootValue, 143 schemasTable *WritableDoltTable, 144 ) ( 145 *doltdb.RootValue, 146 []sql.Row, 147 error, 148 ) { 149 // Copy all of the old data over and add an index column 150 var rowsToAdd []sql.Row 151 table, err := schemasTable.doltTable(ctx) 152 if err != nil { 153 return nil, nil, err 154 } 155 156 rowData, err := table.GetRowData(ctx) 157 if err != nil { 158 return nil, nil, err 159 } 160 id := int64(1) 161 err = rowData.IterAll(ctx, func(key, val types.Value) error { 162 dRow, err := row.FromNoms(schemasTable.sch, key.(types.Tuple), val.(types.Tuple)) 163 if err != nil { 164 return err 165 } 166 sqlRow, err := sqlutil.DoltRowToSqlRow(dRow, schemasTable.sch) 167 if err != nil { 168 return err 169 } 170 // prepend the new id to each row 171 sqlRow = append(sqlRow, id) 172 rowsToAdd = append(rowsToAdd, sqlRow) 173 id++ 174 return nil 175 }) 176 if err != nil { 177 return nil, nil, err 178 } 179 err = db.DropTable(ctx, doltdb.SchemasTableName) 180 if err != nil { 181 return nil, nil, err 182 } 183 root, err = db.GetRoot(ctx) 184 if err != nil { 185 return nil, nil, err 186 } 187 return root, rowsToAdd, nil 188 } 189 190 // fragFromSchemasTable returns the row with the given schema fragment if it exists. 191 func fragFromSchemasTable(ctx *sql.Context, tbl *WritableDoltTable, fragType string, name string) (sql.Row, bool, error) { 192 indexes, err := tbl.GetIndexes(ctx) 193 if err != nil { 194 return nil, false, err 195 } 196 var fragNameIndex sql.Index 197 for _, index := range indexes { 198 if index.ID() == doltdb.SchemasTablesIndexName { 199 fragNameIndex = index 200 break 201 } 202 } 203 if fragNameIndex == nil { 204 return nil, false, fmt.Errorf("could not find index `%s` on system table `%s`", doltdb.SchemasTablesIndexName, doltdb.SchemasTableName) 205 } 206 207 indexLookup, err := fragNameIndex.Get(fragType, name) 208 if err != nil { 209 return nil, false, err 210 } 211 dil := indexLookup.(*doltIndexLookup) 212 rowIter, err := dil.RowIter(ctx, dil.IndexRowData(), nil) 213 if err != nil { 214 return nil, false, err 215 } 216 defer rowIter.Close(ctx) 217 sqlRow, err := rowIter.Next() 218 if err == nil { 219 return sqlRow, true, nil 220 } else if err == io.EOF { 221 return nil, false, nil 222 } else { 223 return nil, false, err 224 } 225 }