github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/table_editor.go (about)

     1  // Copyright 2019 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  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    19  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    20  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
    21  	"github.com/dolthub/dolt/go/store/types"
    22  
    23  	"github.com/dolthub/go-mysql-server/sql"
    24  
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
    27  )
    28  
    29  // sqlTableEditor is a wrapper for *doltdb.SessionedTableEditor that complies with the SQL interface.
    30  //
    31  // The sqlTableEditor has two levels of batching: one supported at the SQL engine layer where a single UPDATE, DELETE or
    32  // INSERT statement will touch many rows, and we want to avoid unnecessary intermediate writes; and one at the dolt
    33  // layer as a "batch mode" in DoltDatabase. In the latter mode, it's possible to mix inserts, updates and deletes in any
    34  // order. In general, this is unsafe and will produce incorrect results in many cases. The editor makes reasonable
    35  // attempts to produce correct results when interleaving insert and delete statements, but this is almost entirely to
    36  // support REPLACE statements, which are implemented as a DELETE followed by an INSERT. In general, not flushing the
    37  // editor after every SQL statement is incorrect and will return incorrect results. The single reliable exception is an
    38  // unbroken chain of INSERT statements, where we have taken pains to batch writes to speed things up.
    39  type sqlTableEditor struct {
    40  	tableName         string
    41  	dbName            string
    42  	sch               schema.Schema
    43  	autoIncCol        schema.Column
    44  	vrw               types.ValueReadWriter
    45  	autoIncrementType typeinfo.TypeInfo
    46  	kvToSQLRow        *KVToSqlRowConverter
    47  	tableEditor       editor.TableEditor
    48  	sess              *editor.TableEditSession
    49  	temporary         bool
    50  }
    51  
    52  var _ sql.RowReplacer = (*sqlTableEditor)(nil)
    53  var _ sql.RowUpdater = (*sqlTableEditor)(nil)
    54  var _ sql.RowInserter = (*sqlTableEditor)(nil)
    55  var _ sql.RowDeleter = (*sqlTableEditor)(nil)
    56  
    57  func newSqlTableEditor(ctx *sql.Context, t *WritableDoltTable) (*sqlTableEditor, error) {
    58  	sess := t.db.TableEditSession(ctx, t.IsTemporary())
    59  
    60  	tableEditor, err := sess.GetTableEditor(ctx, t.tableName, t.sch)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	conv := NewKVToSqlRowConverterForCols(t.nbf, t.sch.GetAllCols().GetColumns())
    66  	return &sqlTableEditor{
    67  		tableName:   t.Name(),
    68  		dbName:      t.db.Name(),
    69  		sch:         t.sch,
    70  		autoIncCol:  t.autoIncCol,
    71  		vrw:         t.db.ddb.ValueReadWriter(),
    72  		kvToSQLRow:  conv,
    73  		tableEditor: tableEditor,
    74  		sess:        sess,
    75  		temporary:   t.IsTemporary(),
    76  	}, nil
    77  }
    78  
    79  func (te *sqlTableEditor) duplicateKeyErrFunc(keyString string, k, v types.Tuple, isPk bool) error {
    80  	oldRow, err := te.kvToSQLRow.ConvertKVTuplesToSqlRow(k, v)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	return sql.NewUniqueKeyErr(keyString, isPk, oldRow)
    86  }
    87  
    88  func (te *sqlTableEditor) Insert(ctx *sql.Context, sqlRow sql.Row) error {
    89  	if !schema.IsKeyless(te.sch) {
    90  		k, v, tagToVal, err := sqlutil.DoltKeyValueAndMappingFromSqlRow(ctx, te.vrw, sqlRow, te.sch)
    91  
    92  		if err != nil {
    93  			return err
    94  		}
    95  
    96  		return te.tableEditor.InsertKeyVal(ctx, k, v, tagToVal, te.duplicateKeyErrFunc)
    97  	}
    98  
    99  	dRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, sqlRow, te.sch)
   100  
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	return te.tableEditor.InsertRow(ctx, dRow, te.duplicateKeyErrFunc)
   106  }
   107  
   108  func (te *sqlTableEditor) Delete(ctx *sql.Context, sqlRow sql.Row) error {
   109  	dRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, sqlRow, te.sch)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	return te.tableEditor.DeleteRow(ctx, dRow)
   115  }
   116  
   117  func (te *sqlTableEditor) Update(ctx *sql.Context, oldRow sql.Row, newRow sql.Row) error {
   118  	dOldRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, oldRow, te.sch)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	dNewRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, newRow, te.sch)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	return te.tableEditor.UpdateRow(ctx, dOldRow, dNewRow, te.duplicateKeyErrFunc)
   128  }
   129  
   130  func (te *sqlTableEditor) GetAutoIncrementValue() (interface{}, error) {
   131  	val := te.tableEditor.GetAutoIncrementValue()
   132  	return te.autoIncCol.TypeInfo.ConvertNomsValueToValue(val)
   133  }
   134  
   135  func (te *sqlTableEditor) SetAutoIncrementValue(ctx *sql.Context, val interface{}) error {
   136  	nomsVal, err := te.autoIncCol.TypeInfo.ConvertValueToNomsValue(ctx, te.vrw, val)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	if err = te.tableEditor.SetAutoIncrementValue(nomsVal); err != nil {
   141  		return err
   142  	}
   143  	return te.flush(ctx)
   144  }
   145  
   146  // Close implements Closer
   147  func (te *sqlTableEditor) Close(ctx *sql.Context) error {
   148  	sess := DSessFromSess(ctx.Session)
   149  
   150  	// If we're running in batched mode, don't flush the edits until explicitly told to do so
   151  	if sess.batchMode == batched {
   152  		return nil
   153  	}
   154  	return te.flush(ctx)
   155  }
   156  
   157  // StatementBegin implements the interface sql.TableEditor.
   158  func (te *sqlTableEditor) StatementBegin(ctx *sql.Context) {
   159  	te.tableEditor.StatementStarted(ctx)
   160  }
   161  
   162  // DiscardChanges implements the interface sql.TableEditor.
   163  func (te *sqlTableEditor) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
   164  	return te.tableEditor.StatementFinished(ctx, true)
   165  }
   166  
   167  // StatementComplete implements the interface sql.TableEditor.
   168  func (te *sqlTableEditor) StatementComplete(ctx *sql.Context) error {
   169  	return te.tableEditor.StatementFinished(ctx, false)
   170  }
   171  
   172  func (te *sqlTableEditor) flush(ctx *sql.Context) error {
   173  	newRoot, err := te.sess.Flush(ctx)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	return te.setRoot(ctx, newRoot)
   179  }
   180  
   181  func (te *sqlTableEditor) setRoot(ctx *sql.Context, newRoot *doltdb.RootValue) error {
   182  	dSess := DSessFromSess(ctx.Session)
   183  
   184  	if te.temporary {
   185  		return dSess.SetTempTableRoot(ctx, te.dbName, newRoot)
   186  	}
   187  
   188  	return dSess.SetRoot(ctx, te.dbName, newRoot)
   189  }