-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathcontracts.go
136 lines (114 loc) · 4.74 KB
/
contracts.go
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
package ksql
import (
"context"
"database/sql"
"fmt"
"github.com/vingarcia/ksql/sqldialect"
)
// ErrRecordNotFound informs that a given query failed because the record was not found.
// This error can be returned by the following methods: Patch(), QueryOne() and Delete().
var ErrRecordNotFound error = fmt.Errorf("ksql: the query returned no results: %w", sql.ErrNoRows)
// ErrNoValuesToUpdate informs the error of trying to make an update that would not change any attributes.
//
// This could happen if all the non-ID attributes of the struct are being ignored or if they don't exist.
//
// Since ID attributes are ignored by the Patch() method updating a struct that only have IDs will result in this error.
// And this error will also occur if the struct does have some non-ID attributes but they are all being ignored.
//
// The reasons that can cause an attribute to be ignored in the Patch() function are:
// (1) If it is a nil pointer, Patch() will just ignore it.
// (2) If the attribute is using a modifier that contains the SkipUpdates flag.
var ErrNoValuesToUpdate error = fmt.Errorf("ksql: the input struct contains no values to update")
// ErrRecordMissingIDs is returned by the Update or Delete functions if an input record does
// not have all of the IDs described on the input table.
var ErrRecordMissingIDs error = fmt.Errorf("ksql: missing required ID fields")
// ErrAbortIteration should be used inside the QueryChunks function to inform QueryChunks it should stop querying,
// close the connection and return with no errors.
var ErrAbortIteration error = fmt.Errorf("ksql: abort iteration, should only be used inside QueryChunks function")
// Provider describes the ksql public behavior.
//
// The Insert, Update, Delete and QueryOne functions return ksql.ErrRecordNotFound
// if no record was found or no rows were changed during the operation.
type Provider interface {
Insert(ctx context.Context, table Table, record interface{}) error
Patch(ctx context.Context, table Table, record interface{}) error
Delete(ctx context.Context, table Table, idOrRecord interface{}) error
Query(ctx context.Context, records interface{}, query string, params ...interface{}) error
QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error
QueryChunks(ctx context.Context, parser ChunkParser) error
Exec(ctx context.Context, query string, params ...interface{}) (Result, error)
Transaction(ctx context.Context, fn func(Provider) error) error
}
// Table describes the required information for inserting, updating and
// deleting entities from the database by ID using the 3 helper functions
// created for that purpose.
type Table struct {
// this name must be set in order to use the Insert, Delete and Patch helper
// functions. If you only intend to make queries or to use the Exec function
// it is safe to leave this field unset.
name string
// IDColumns defaults to []string{"id"} if unset
idColumns []string
}
// NewTable returns a Table instance that stores
// the tablename and the names of columns used as ID,
// if no column name is passed it defaults to using
// the `"id"` column.
//
// This Table is required only for using the helper methods:
//
// - Insert
// - Patch
// - Delete
//
// Passing multiple ID columns will be interpreted
// as a single composite key, if you want
// to use the helper functions with different
// keys you'll need to create multiple Table instances
// for the same database table, each with a different
// set of ID columns, but this is usually not necessary.
func NewTable(tableName string, ids ...string) Table {
if len(ids) == 0 {
ids = []string{"id"}
}
return Table{
name: tableName,
idColumns: ids,
}
}
func (t Table) validate() error {
if t.name == "" {
return fmt.Errorf("table name cannot be an empty string")
}
for _, fieldName := range t.idColumns {
if fieldName == "" {
return fmt.Errorf("ID columns cannot be empty strings")
}
}
return nil
}
func (t Table) insertMethodFor(dialect sqldialect.Provider) sqldialect.InsertMethod {
if len(t.idColumns) == 1 {
return dialect.InsertMethod()
}
insertMethod := dialect.InsertMethod()
if insertMethod == sqldialect.InsertWithLastInsertID {
return sqldialect.InsertWithNoIDRetrieval
}
return insertMethod
}
// ChunkParser stores the arguments of the QueryChunks function
type ChunkParser struct {
// The Query and Params are used together to build a query with
// protection from injection, just like when using the Find function.
Query string
Params []interface{}
ChunkSize int
// This attribute must be a function with the following signature:
//
// `func(chunk []<Record>) error`.
//
// Where the actual Record type should be of a struct
// representing the rows you are expecting to receive.
ForEachChunk interface{}
}