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 }