File: mapper_boilerplate.go

package info (click to toggle)
incus 6.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,392 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (196 lines) | stat: -rw-r--r-- 4,962 bytes parent folder | download | duplicates (2)
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Code generated by generate-database from the incus project - DO NOT EDIT.

package cluster

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
)

type tx interface { //nolint:unused
	dbtx

	Commit() error
	Rollback() error
}

type dbtx interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}

type preparer interface {
	Prepare(query string) (*sql.Stmt, error)
}

// RegisterStmt register a SQL statement.
//
// Registered statements will be prepared upfront and reused, to speed up
// execution.
//
// Return a unique registration code.
func RegisterStmt(sqlStmt string) int {
	code := len(stmts)
	stmts[code] = sqlStmt
	return code
}

// PrepareStmts prepares all registered statements and returns an index from
// statement code to prepared statement object.
func PrepareStmts(db preparer, skipErrors bool) (map[int]*sql.Stmt, error) {
	index := map[int]*sql.Stmt{}

	for code, sqlStmt := range stmts {
		stmt, err := db.Prepare(sqlStmt)
		if err != nil && !skipErrors {
			return nil, fmt.Errorf("%q: %w", sqlStmt, err)
		}

		index[code] = stmt
	}

	return index, nil
}

var stmts = map[int]string{} // Statement code to statement SQL text.

// PreparedStmts is a placeholder for transitioning to package-scoped transaction functions.
var PreparedStmts = map[int]*sql.Stmt{}

// Stmt prepares the in-memory prepared statement for the transaction.
func Stmt(db dbtx, code int) (*sql.Stmt, error) {
	stmt, ok := PreparedStmts[code]
	if !ok {
		return nil, fmt.Errorf("No prepared statement registered with code %d", code)
	}

	tx, ok := db.(*sql.Tx)
	if ok {
		return tx.Stmt(stmt), nil
	}

	return stmt, nil
}

// StmtString returns the in-memory query string with the given code.
func StmtString(code int) (string, error) {
	stmt, ok := stmts[code]
	if !ok {
		return "", fmt.Errorf("No prepared statement registered with code %d", code)
	}

	return stmt, nil
}

var (
	// ErrNotFound is the error returned, if the entity is not found in the DB.
	ErrNotFound = errors.New("Not found")

	// ErrConflict is the error returned, if the adding or updating an entity
	// causes a conflict with an existing entity.
	ErrConflict = errors.New("Conflict")
)

var mapErr = defaultMapErr

func defaultMapErr(err error, entity string) error {
	return err
}

// Marshaler is the interface that wraps the MarshalDB method, which converts
// the underlying type into a string representation suitable for persistence in
// the database.
type Marshaler interface {
	MarshalDB() (string, error)
}

// Unmarshaler is the interface that wraps the UnmarshalDB method, which converts
// a string representation retrieved from the database into the underlying type.
type Unmarshaler interface {
	UnmarshalDB(string) error
}

func marshal(v any) (string, error) {
	marshaller, ok := v.(Marshaler)
	if !ok {
		return "", errors.New("Cannot marshal data, type does not implement DBMarshaler")
	}

	return marshaller.MarshalDB()
}

func unmarshal(data string, v any) error {
	if v == nil {
		return errors.New("Cannot unmarshal data into nil value")
	}

	unmarshaler, ok := v.(Unmarshaler)
	if !ok {
		return errors.New("Cannot marshal data, type does not implement DBUnmarshaler")
	}

	return unmarshaler.UnmarshalDB(data)
}

func marshalJSON(v any) (string, error) {
	marshalled, err := json.Marshal(v)
	if err != nil {
		return "", err
	}

	return string(marshalled), nil
}

func unmarshalJSON(data string, v any) error {
	return json.Unmarshal([]byte(data), v)
}

// dest is a function that is expected to return the objects to pass to the
// 'dest' argument of sql.Rows.Scan(). It is invoked by SelectObjects once per
// yielded row, and it will be passed the index of the row being scanned.
type dest func(scan func(dest ...any) error) error

// selectObjects executes a statement which must yield rows with a specific
// columns schema. It invokes the given Dest hook for each yielded row.
func selectObjects(ctx context.Context, stmt *sql.Stmt, rowFunc dest, args ...any) error {
	rows, err := stmt.QueryContext(ctx, args...)
	if err != nil {
		return err
	}

	defer func() { _ = rows.Close() }()

	for rows.Next() {
		err = rowFunc(rows.Scan)
		if err != nil {
			return err
		}
	}

	return rows.Err()
}

// scan runs a query with inArgs and provides the rowFunc with the scan function for each row.
// It handles closing the rows and errors from the result set.
func scan(ctx context.Context, db dbtx, sqlStmt string, rowFunc dest, inArgs ...any) error {
	rows, err := db.QueryContext(ctx, sqlStmt, inArgs...)
	if err != nil {
		return err
	}

	defer func() { _ = rows.Close() }()

	for rows.Next() {
		err = rowFunc(rows.Scan)
		if err != nil {
			return err
		}
	}

	return rows.Err()
}