hermez-node/db/utils.go
db/utils.goRaw
1package db
2
3import (
4 "encoding/base64"
5 "fmt"
6 "math/big"
7 "reflect"
8 "strings"
9
10 "github.com/russross/meddler"
11)
12
13// InitMeddler registers tags to be used to read/write from SQL DBs using meddler
14func InitMeddler() {
15 meddler.Register("bigint", BigIntMeddler{})
16 meddler.Register("bigintnull", BigIntNullMeddler{})
17}
18
19// BulkInsert performs a bulk insert with a single statement into the specified table. Example:
20// `db.BulkInsert(myDB, "INSERT INTO block (eth_block_num, timestamp, hash) VALUES %s", blocks[:])`
21// Note that all the columns must be specified in the query, and they must be in the same order as in the table.
22func BulkInsert(db meddler.DB, q string, args interface{}) error {
23 arrayValue := reflect.ValueOf(args)
24 arrayLen := arrayValue.Len()
25 valueStrings := make([]string, 0, arrayLen)
26 var arglist = make([]interface{}, 0)
27 for i := 0; i < arrayLen; i++ {
28 arg := arrayValue.Index(i).Addr().Interface()
29 elemArglist, err := meddler.Default.Values(arg, true)
30 if err != nil {
31 return err
32 }
33 arglist = append(arglist, elemArglist...)
34 value := "("
35 for j := 0; j < len(elemArglist); j++ {
36 value += fmt.Sprintf("$%d, ", i*len(elemArglist)+j+1)
37 }
38 value = value[:len(value)-2] + ")"
39 valueStrings = append(valueStrings, value)
40 }
41 stmt := fmt.Sprintf(q, strings.Join(valueStrings, ","))
42 _, err := db.Exec(stmt, arglist...)
43 return err
44}
45
46// BigIntMeddler encodes or decodes the field value to or from JSON
47type BigIntMeddler struct{}
48
49// PreRead is called before a Scan operation for fields that have the BigIntMeddler
50func (b BigIntMeddler) PreRead(fieldAddr interface{}) (scanTarget interface{}, err error) {
51 // give a pointer to a byte buffer to grab the raw data
52 return new(string), nil
53}
54
55// PostRead is called after a Scan operation for fields that have the BigIntMeddler
56func (b BigIntMeddler) PostRead(fieldPtr, scanTarget interface{}) error {
57 ptr := scanTarget.(*string)
58 if ptr == nil {
59 return fmt.Errorf("BigIntMeddler.PostRead: nil pointer")
60 }
61
62 data, err := base64.StdEncoding.DecodeString(*ptr)
63 if err != nil {
64 return fmt.Errorf("big.Int decode error: %v", err)
65 }
66 field := fieldPtr.(**big.Int)
67 *field = new(big.Int).SetBytes(data)
68
69 return nil
70}
71
72// PreWrite is called before an Insert or Update operation for fields that have the BigIntMeddler
73func (b BigIntMeddler) PreWrite(fieldPtr interface{}) (saveValue interface{}, err error) {
74 field := fieldPtr.(*big.Int)
75
76 str := base64.StdEncoding.EncodeToString(field.Bytes())
77
78 return str, nil
79}
80
81// BigIntNullMeddler encodes or decodes the field value to or from JSON
82type BigIntNullMeddler struct{}
83
84// PreRead is called before a Scan operation for fields that have the BigIntNullMeddler
85func (b BigIntNullMeddler) PreRead(fieldAddr interface{}) (scanTarget interface{}, err error) {
86 return &fieldAddr, nil
87}
88
89// PostRead is called after a Scan operation for fields that have the BigIntNullMeddler
90func (b BigIntNullMeddler) PostRead(fieldPtr, scanTarget interface{}) error {
91 sv := reflect.ValueOf(scanTarget)
92 if sv.Elem().IsNil() {
93 // null column, so set target to be zero value
94 fv := reflect.ValueOf(fieldPtr)
95 fv.Elem().Set(reflect.Zero(fv.Elem().Type()))
96 return nil
97 }
98 // not null
99 encoded := new([]byte)
100 refEnc := reflect.ValueOf(encoded)
101 refEnc.Elem().Set(sv.Elem().Elem())
102 data, err := base64.StdEncoding.DecodeString(string(*encoded))
103 if err != nil {
104 return fmt.Errorf("big.Int decode error: %v", err)
105 }
106 field := fieldPtr.(**big.Int)
107 *field = new(big.Int).SetBytes(data)
108 return nil
109}
110
111// PreWrite is called before an Insert or Update operation for fields that have the BigIntNullMeddler
112func (b BigIntNullMeddler) PreWrite(fieldPtr interface{}) (saveValue interface{}, err error) {
113 field := fieldPtr.(*big.Int)
114 if field == nil {
115 return nil, nil
116 }
117 return base64.StdEncoding.EncodeToString(field.Bytes()), nil
118}