github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/table/tables/bounded_tables.go (about) 1 // Copyright 2016 PingCAP, 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package tables 15 16 import ( 17 "sync/atomic" 18 "unsafe" 19 20 "github.com/insionng/yougam/libraries/juju/errors" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/column" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/context" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/meta/autoid" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/table" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 28 ) 29 30 const ( 31 // We want to ensure our initial record id greater than a threshold. 32 initialRecordID int64 = 123 33 // In our case, a valid record will always greater than 0, so we use a 34 // macro to mark an invalid one. 35 invalidRecordID int64 = 0 36 ) 37 38 var ( 39 errOpNotSupported = errors.New("Operation not supported") 40 ) 41 42 type boundedItem struct { 43 handle int64 44 data []types.Datum 45 } 46 47 // BoundedTable implements table.Table interface. 48 type BoundedTable struct { 49 // cursor must be aligned in i386, or we will meet panic when calling atomic.AddInt64 50 // https://yougam/libraries/src/sync/atomic/doc.go#L41 51 cursor int64 52 ID int64 53 Name model.CIStr 54 Columns []*column.Col 55 pkHandleCol *column.Col 56 57 recordPrefix kv.Key 58 alloc autoid.Allocator 59 meta *model.TableInfo 60 61 records []unsafe.Pointer 62 capacity int64 63 } 64 65 // BoundedTableFromMeta creates a Table instance from model.TableInfo. 66 func BoundedTableFromMeta(alloc autoid.Allocator, tblInfo *model.TableInfo, capacity int64) table.Table { 67 columns := make([]*column.Col, 0, len(tblInfo.Columns)) 68 var pkHandleColumn *column.Col 69 for _, colInfo := range tblInfo.Columns { 70 col := &column.Col{ColumnInfo: *colInfo} 71 columns = append(columns, col) 72 if col.IsPKHandleColumn(tblInfo) { 73 pkHandleColumn = col 74 } 75 } 76 t := newBoundedTable(tblInfo.ID, tblInfo.Name.O, columns, alloc, capacity) 77 t.pkHandleCol = pkHandleColumn 78 t.meta = tblInfo 79 return t 80 } 81 82 // newBoundedTable constructs a BoundedTable instance. 83 func newBoundedTable(tableID int64, tableName string, cols []*column.Col, alloc autoid.Allocator, capacity int64) *BoundedTable { 84 name := model.NewCIStr(tableName) 85 t := &BoundedTable{ 86 ID: tableID, 87 Name: name, 88 alloc: alloc, 89 Columns: cols, 90 recordPrefix: genTableRecordPrefix(tableID), 91 records: make([]unsafe.Pointer, capacity), 92 capacity: capacity, 93 cursor: 0, 94 } 95 return t 96 } 97 98 // newBoundedItem constructs a boundedItem instance. 99 func newBoundedItem(handle int64, data []types.Datum) *boundedItem { 100 i := &boundedItem{ 101 handle: handle, 102 data: data, 103 } 104 return i 105 } 106 107 // Seek seeks the handle. 108 func (t *BoundedTable) Seek(ctx context.Context, handle int64) (int64, bool, error) { 109 result := (*boundedItem)(nil) 110 if handle < invalidRecordID { 111 // this is the first seek call. 112 result = (*boundedItem)(atomic.LoadPointer(&t.records[0])) 113 } else { 114 for i := int64(0); i < t.capacity; i++ { 115 record := (*boundedItem)(atomic.LoadPointer(&t.records[i])) 116 if record == nil { 117 break 118 } 119 if handle == record.handle { 120 result = record 121 break 122 } 123 } 124 } 125 if result == nil { 126 // handle not found. 127 return invalidRecordID, false, nil 128 } 129 if result.handle != invalidRecordID { 130 // this record is valid. 131 return result.handle, true, nil 132 } 133 // this record is invalid. 134 return invalidRecordID, false, nil 135 } 136 137 // Indices implements table.Table Indices interface. 138 func (t *BoundedTable) Indices() []*column.IndexedCol { 139 return nil 140 } 141 142 // Meta implements table.Table Meta interface. 143 func (t *BoundedTable) Meta() *model.TableInfo { 144 return t.meta 145 } 146 147 // Cols implements table.Table Cols interface. 148 func (t *BoundedTable) Cols() []*column.Col { 149 return t.Columns 150 } 151 152 // RecordPrefix implements table.Table RecordPrefix interface. 153 func (t *BoundedTable) RecordPrefix() kv.Key { 154 return t.recordPrefix 155 } 156 157 // IndexPrefix implements table.Table IndexPrefix interface. 158 func (t *BoundedTable) IndexPrefix() kv.Key { 159 return nil 160 } 161 162 // RecordKey implements table.Table RecordKey interface. 163 func (t *BoundedTable) RecordKey(h int64, col *column.Col) kv.Key { 164 colID := int64(0) 165 if col != nil { 166 colID = col.ID 167 } 168 return encodeRecordKey(t.recordPrefix, h, colID) 169 } 170 171 // FirstKey implements table.Table FirstKey interface. 172 func (t *BoundedTable) FirstKey() kv.Key { 173 return t.RecordKey(0, nil) 174 } 175 176 // Truncate implements table.Table Truncate interface. 177 func (t *BoundedTable) Truncate(ctx context.Context) error { 178 // just reset everything. 179 for i := int64(0); i < t.capacity; i++ { 180 atomic.StorePointer(&t.records[i], unsafe.Pointer(nil)) 181 } 182 t.cursor = 0 183 return nil 184 } 185 186 // UpdateRecord implements table.Table UpdateRecord interface. 187 func (t *BoundedTable) UpdateRecord(ctx context.Context, h int64, oldData []types.Datum, newData []types.Datum, touched map[int]bool) error { 188 for i := int64(0); i < t.capacity; i++ { 189 record := (*boundedItem)(atomic.LoadPointer(&t.records[i])) 190 if record == nil { 191 // A nil record means consecutive nil records. 192 break 193 } 194 if record.handle == h { 195 newRec := newBoundedItem(h, newData) 196 atomic.StorePointer(&t.records[i], unsafe.Pointer(newRec)) 197 break 198 } 199 } 200 // UPDATE always succeeds for BoundedTable. 201 return nil 202 } 203 204 // AddRecord implements table.Table AddRecord interface. 205 func (t *BoundedTable) AddRecord(ctx context.Context, r []types.Datum) (int64, error) { 206 var recordID int64 207 var err error 208 if t.pkHandleCol != nil { 209 recordID, err = types.ToInt64(r[t.pkHandleCol.Offset].GetValue()) 210 if err != nil { 211 return invalidRecordID, errors.Trace(err) 212 } 213 } else { 214 recordID, err = t.alloc.Alloc(t.ID) 215 if err != nil { 216 return invalidRecordID, errors.Trace(err) 217 } 218 } 219 cursor := atomic.AddInt64(&t.cursor, 1) - 1 220 index := int64(cursor % t.capacity) 221 record := newBoundedItem(recordID+initialRecordID, r) 222 atomic.StorePointer(&t.records[index], unsafe.Pointer(record)) 223 return recordID + initialRecordID, nil 224 } 225 226 // RowWithCols implements table.Table RowWithCols interface. 227 func (t *BoundedTable) RowWithCols(ctx context.Context, h int64, cols []*column.Col) ([]types.Datum, error) { 228 row := []types.Datum(nil) 229 for i := int64(0); i < t.capacity; i++ { 230 record := (*boundedItem)(atomic.LoadPointer(&t.records[i])) 231 if record == nil { 232 // A nil record means consecutive nil records. 233 break 234 } 235 if record.handle == h { 236 row = record.data 237 break 238 } 239 } 240 if row == nil { 241 return nil, errRowNotFound 242 } 243 v := make([]types.Datum, len(cols)) 244 for i, col := range cols { 245 if col == nil { 246 continue 247 } 248 v[i] = row[col.Offset] 249 } 250 return v, nil 251 } 252 253 // Row implements table.Table Row interface. 254 func (t *BoundedTable) Row(ctx context.Context, h int64) ([]types.Datum, error) { 255 r, err := t.RowWithCols(nil, h, t.Cols()) 256 if err != nil { 257 return nil, errors.Trace(err) 258 } 259 return r, nil 260 } 261 262 // LockRow implements table.Table LockRow interface. 263 func (t *BoundedTable) LockRow(ctx context.Context, h int64, forRead bool) error { 264 return nil 265 } 266 267 // RemoveRecord implements table.Table RemoveRecord interface. 268 func (t *BoundedTable) RemoveRecord(ctx context.Context, h int64, r []types.Datum) error { 269 // not supported, BoundedTable is TRUNCATE only 270 return errOpNotSupported 271 } 272 273 // AllocAutoID implements table.Table AllocAutoID interface. 274 func (t *BoundedTable) AllocAutoID() (int64, error) { 275 recordID, err := t.alloc.Alloc(t.ID) 276 if err != nil { 277 return invalidRecordID, errors.Trace(err) 278 } 279 return recordID + initialRecordID, nil 280 } 281 282 // RebaseAutoID implements table.Table RebaseAutoID interface. 283 func (t *BoundedTable) RebaseAutoID(newBase int64, isSetStep bool) error { 284 return t.alloc.Rebase(t.ID, newBase, isSetStep) 285 } 286 287 // IterRecords implements table.Table IterRecords interface. 288 func (t *BoundedTable) IterRecords(ctx context.Context, startKey kv.Key, cols []*column.Col, 289 fn table.RecordIterFunc) error { 290 return nil 291 }