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  }