github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/writer/noms_table_writer.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 writer 16 17 import ( 18 "github.com/dolthub/go-mysql-server/sql" 19 20 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/globalstate" 21 22 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 23 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 24 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" 25 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 26 "github.com/dolthub/dolt/go/libraries/doltcore/table/editor" 27 "github.com/dolthub/dolt/go/store/types" 28 ) 29 30 type TableWriter interface { 31 sql.TableEditor 32 sql.ForeignKeyEditor 33 sql.AutoIncrementSetter 34 } 35 36 // AutoIncrementGetter is implemented by editors that support AUTO_INCREMENT to return the next value that will be 37 // inserted. 38 type AutoIncrementGetter interface { 39 GetNextAutoIncrementValue(ctx *sql.Context, insertVal interface{}) (uint64, error) 40 } 41 42 // SessionRootSetter sets the root value for the session. 43 type SessionRootSetter func(ctx *sql.Context, dbName string, root doltdb.RootValue) error 44 45 // nomsTableWriter is a wrapper for *doltdb.SessionedTableEditor that complies with the SQL interface. 46 // 47 // The nomsTableWriter has two levels of batching: one supported at the SQL engine layer where a single UPDATE, DELETE or 48 // INSERT statement will touch many rows, and we want to avoid unnecessary intermediate writes; and one at the dolt 49 // layer as a "batch mode" in DoltDatabase. In the latter mode, it's possible to mix inserts, updates and deletes in any 50 // order. In general, this is unsafe and will produce incorrect results in many cases. The editor makes reasonable 51 // attempts to produce correct results when interleaving insert and delete statements, but this is almost entirely to 52 // support REPLACE statements, which are implemented as a DELETE followed by an INSERT. In general, not flushing the 53 // editor after every SQL statement is incorrect and will return incorrect results. The single reliable exception is an 54 // unbroken chain of INSERT statements, where we have taken pains to batch writes to speed things up. 55 type nomsTableWriter struct { 56 tableName string 57 dbName string 58 sch schema.Schema 59 sqlSch sql.Schema 60 vrw types.ValueReadWriter 61 kvToSQLRow *index.KVToSqlRowConverter 62 tableEditor editor.TableEditor 63 flusher WriteSessionFlusher 64 65 autoInc globalstate.AutoIncrementTracker 66 nextAutoIncrementValue map[string]uint64 67 68 setter SessionRootSetter 69 errEncountered error 70 } 71 72 func (te *nomsTableWriter) PreciseMatch() bool { 73 return true 74 } 75 76 var _ TableWriter = &nomsTableWriter{} 77 var _ AutoIncrementGetter = &nomsTableWriter{} 78 79 func (te *nomsTableWriter) duplicateKeyErrFunc(keyString, indexName 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 *nomsTableWriter) Insert(ctx *sql.Context, sqlRow sql.Row) error { 89 if schema.IsKeyless(te.sch) { 90 return te.keylessInsert(ctx, sqlRow) 91 } 92 return te.keyedInsert(ctx, sqlRow) 93 } 94 95 func (te *nomsTableWriter) keylessInsert(ctx *sql.Context, sqlRow sql.Row) error { 96 dRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, sqlRow, te.sch) 97 if err != nil { 98 return err 99 } 100 return te.tableEditor.InsertRow(ctx, dRow, te.duplicateKeyErrFunc) 101 } 102 103 func (te *nomsTableWriter) keyedInsert(ctx *sql.Context, sqlRow sql.Row) error { 104 k, v, tagToVal, err := sqlutil.DoltKeyValueAndMappingFromSqlRow(ctx, te.vrw, sqlRow, te.sch) 105 if err != nil { 106 return err 107 } 108 return te.tableEditor.InsertKeyVal(ctx, k, v, tagToVal, te.duplicateKeyErrFunc) 109 } 110 111 func (te *nomsTableWriter) Delete(ctx *sql.Context, sqlRow sql.Row) error { 112 if !schema.IsKeyless(te.sch) { 113 k, tagToVal, err := sqlutil.DoltKeyAndMappingFromSqlRow(ctx, te.vrw, sqlRow, te.sch) 114 if err != nil { 115 return err 116 } 117 return te.tableEditor.DeleteByKey(ctx, k, tagToVal) 118 } else { 119 dRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, sqlRow, te.sch) 120 if err != nil { 121 return err 122 } 123 return te.tableEditor.DeleteRow(ctx, dRow) 124 } 125 } 126 127 func (te *nomsTableWriter) Update(ctx *sql.Context, oldRow sql.Row, newRow sql.Row) error { 128 dOldRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, oldRow, te.sch) 129 if err != nil { 130 return err 131 } 132 dNewRow, err := sqlutil.SqlRowToDoltRow(ctx, te.vrw, newRow, te.sch) 133 if err != nil { 134 return err 135 } 136 137 return te.tableEditor.UpdateRow(ctx, dOldRow, dNewRow, te.duplicateKeyErrFunc) 138 } 139 140 func (te *nomsTableWriter) GetNextAutoIncrementValue(ctx *sql.Context, insertVal interface{}) (uint64, error) { 141 return te.autoInc.Next(te.tableName, insertVal) 142 } 143 144 func (te *nomsTableWriter) SetAutoIncrementValue(ctx *sql.Context, val uint64) error { 145 seq, err := te.autoInc.CoerceAutoIncrementValue(val) 146 if err != nil { 147 return err 148 } 149 150 te.nextAutoIncrementValue = make(map[string]uint64) 151 te.nextAutoIncrementValue[te.tableName] = seq 152 153 return te.flush(ctx) 154 } 155 156 func (te *nomsTableWriter) AcquireAutoIncrementLock(ctx *sql.Context) (func(), error) { 157 return te.autoInc.AcquireTableLock(ctx, te.tableName) 158 } 159 160 func (te *nomsTableWriter) IndexedAccess(i sql.IndexLookup) sql.IndexedTable { 161 idx := index.DoltIndexFromSqlIndex(i.Index) 162 return &nomsFkIndexer{ 163 writer: te, 164 idxName: idx.ID(), 165 idxSch: idx.IndexSchema(), 166 } 167 } 168 169 func (te *nomsTableWriter) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { 170 indexes := ctx.GetIndexRegistry().IndexesByTable(te.dbName, te.tableName) 171 ret := make([]sql.Index, len(indexes)) 172 for i := range indexes { 173 ret[i] = indexes[i] 174 } 175 return ret, nil 176 } 177 178 // Close implements Closer 179 func (te *nomsTableWriter) Close(ctx *sql.Context) error { 180 if te.errEncountered == nil { 181 return te.flush(ctx) 182 } 183 return nil 184 } 185 186 // StatementBegin implements the interface sql.TableEditor. 187 func (te *nomsTableWriter) StatementBegin(ctx *sql.Context) { 188 // Table writers are reused in a session, which means we need to reset the error state resulting from previous 189 // errors on every new statement. 190 te.errEncountered = nil 191 te.tableEditor.StatementStarted(ctx) 192 } 193 194 // DiscardChanges implements the interface sql.TableEditor. 195 func (te *nomsTableWriter) DiscardChanges(ctx *sql.Context, errorEncountered error) error { 196 if _, ignored := errorEncountered.(sql.IgnorableError); !ignored { 197 te.errEncountered = errorEncountered 198 } 199 return te.tableEditor.StatementFinished(ctx, true) 200 } 201 202 // StatementComplete implements the interface sql.TableEditor. 203 func (te *nomsTableWriter) StatementComplete(ctx *sql.Context) error { 204 return te.tableEditor.StatementFinished(ctx, false) 205 } 206 207 func (te *nomsTableWriter) flush(ctx *sql.Context) error { 208 ws, err := te.flusher.Flush(ctx) 209 if err != nil { 210 return err 211 } 212 return te.setter(ctx, te.dbName, ws.WorkingRoot()) 213 } 214 215 func autoIncrementColFromSchema(sch schema.Schema) schema.Column { 216 var autoCol schema.Column 217 _ = sch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 218 if col.AutoIncrement { 219 autoCol = col 220 stop = true 221 } 222 return 223 }) 224 return autoCol 225 }