forked from Shopify/ghostferry
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrow_batch.go
More file actions
161 lines (129 loc) · 4.14 KB
/
Copy pathrow_batch.go
File metadata and controls
161 lines (129 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package ghostferry
import (
"fmt"
"strings"
)
type RowBatch interface {
TableSchema() *TableSchema
AsSQLQuery(schemaName, tableName string) (string, []interface{}, error)
Size() int
IsTableComplete() bool
}
type InsertRowBatch interface {
RowBatch
Values() []RowData
VerifierPaginationKey(int) (uint64, error)
Fingerprints() map[uint64][]byte
}
type DataRowBatch struct {
values []RowData
table *TableSchema
fingerprints map[uint64][]byte
}
func NewDataRowBatch(table *TableSchema, values []RowData) *DataRowBatch {
return &DataRowBatch{
values: values,
table: table,
}
}
func (e *DataRowBatch) Values() []RowData {
return e.values
}
func (e *DataRowBatch) Size() int {
return len(e.values)
}
func (e *DataRowBatch) VerifierPaginationKey(rowIndex int) (paginationValue uint64, err error) {
if e.table.PaginationKey == nil {
err = fmt.Errorf("table %s does not have a pagination key", e.table)
} else if e.table.PaginationKey.IsLinearUnsignedKey() {
var value int64
value, err = e.values[rowIndex].GetInt64(e.table.PaginationKey.ColumnIndices[0])
if err == nil {
// for legacy-compatibility, we allow signed pagination keys, so we have to
// make sure no signed data snuck in
if value < 0 {
err = fmt.Errorf("table %s contains an unsupported (signed) pagination key value %d", e.table, value)
} else {
paginationValue = uint64(value)
}
}
} else {
err = UnsupportedPaginationKeyError(e.table.Schema, e.table.Name, e.table.PaginationKey.String())
}
return
}
func (e *DataRowBatch) IsTableComplete() bool {
// we currently always use a dedicated batch for marking completion
return false
}
func (e *DataRowBatch) TableSchema() *TableSchema {
return e.table
}
func (e *DataRowBatch) Fingerprints() map[uint64][]byte {
return e.fingerprints
}
func (e *DataRowBatch) AsSQLQuery(schemaName, tableName string) (string, []interface{}, error) {
if err := verifyValuesHasTheSameLengthAsColumns(e.table, e.values...); err != nil {
return "", nil, err
}
columns := quotedColumnNames(e.table)
valuesStr := "(" + strings.Repeat("?,", len(columns)-1) + "?)"
valuesStr = strings.Repeat(valuesStr+",", len(e.values)-1) + valuesStr
query := "INSERT IGNORE INTO " +
QuotedTableNameFromString(schemaName, tableName) +
" (" + strings.Join(columns, ",") + ") VALUES " + valuesStr
return query, e.flattenRowData(), nil
}
func (e *DataRowBatch) flattenRowData() []interface{} {
rowSize := len(e.values[0])
flattened := make([]interface{}, rowSize*len(e.values))
for rowIdx, row := range e.values {
for colIdx, col := range row {
flattened[rowIdx*rowSize+colIdx] = col
}
}
return flattened
}
type TruncateTableBatch struct {
table *TableSchema
}
func NewTruncateTableBatch(table *TableSchema) *TruncateTableBatch {
return &TruncateTableBatch{table}
}
func (e *TruncateTableBatch) TableSchema() *TableSchema {
return e.table
}
func (e *TruncateTableBatch) Size() int {
return 1
}
func (e *TruncateTableBatch) IsTableComplete() bool {
// we currently always use a dedicated batch for marking completion
return false
}
func (e *TruncateTableBatch) AsSQLQuery(schemaName, tableName string) (string, []interface{}, error) {
quotedTableName := QuotedTableNameFromString(schemaName, tableName)
quotedTableName = strings.Replace(quotedTableName, "*/", "", -1)
query := "TRUNCATE /* ghostferry initialize table " + quotedTableName + " */ TABLE " + quotedTableName
return query, nil, nil
}
type FinalizeTableCopyBatch struct {
table *TableSchema
}
func NewFinalizeTableCopyBatch(table *TableSchema) *FinalizeTableCopyBatch {
return &FinalizeTableCopyBatch{table}
}
func (e *FinalizeTableCopyBatch) TableSchema() *TableSchema {
return e.table
}
func (e *FinalizeTableCopyBatch) Size() int {
return 1
}
func (e *FinalizeTableCopyBatch) IsTableComplete() bool {
return true
}
func (e *FinalizeTableCopyBatch) AsSQLQuery(schemaName, tableName string) (string, []interface{}, error) {
quotedTableName := QuotedTableNameFromString(schemaName, tableName)
quotedTableName = strings.Replace(quotedTableName, "*/", "", -1)
query := "SELECT /* ghostferry finalize table " + quotedTableName + " */ 1"
return query, nil, nil
}