v4 / vlib / db / sqlite / orm.v
301 lines · 272 sloc · 7.61 KB · f581cbb1c964f89133439f4e303136998ddcfcfb
Raw
1module sqlite
2
3import orm
4import time
5
6// select is used internally by V's ORM for processing `SELECT ` queries
7pub fn (db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
8 where_with_tenant := orm.apply_tenant_filter(config.table, where)
9 $if trace_orm_where ? {
10 eprintln('> sqlite.select: where.fields.len = ${where.fields.len}')
11 eprintln('> sqlite.select: where.kinds.len = ${where.kinds.len}')
12 }
13 // 1. Create query and bind necessary data
14 query := orm.orm_select_gen(config, '`', true, '?', 1, where_with_tenant)
15 $if trace_sqlite ? {
16 eprintln('> select query: "${query}"')
17 }
18 stmt := db.new_init_stmt(query)!
19 defer {
20 stmt.finalize()
21 }
22 mut c := 1
23 sqlite_stmt_binder(stmt, where_with_tenant, query, mut c)!
24 sqlite_stmt_binder(stmt, data, query, mut c)!
25
26 mut ret := [][]orm.Primitive{}
27 for {
28 // 2. Parse returned values
29 step := stmt.step()
30 if step == sqlite_done {
31 break
32 }
33 if step != sqlite_ok && step != sqlite_row {
34 break
35 }
36 mut row := []orm.Primitive{}
37 for i, typ in config.types {
38 primitive := stmt.sqlite_select_column(i, typ)!
39 row << primitive
40 }
41 ret << row
42 }
43 return ret
44}
45
46// sql stmt
47
48// insert is used internally by V's ORM for processing `INSERT ` queries
49pub fn (db DB) insert(table orm.Table, data orm.QueryData) ! {
50 query, converted_data :=
51 orm.orm_stmt_gen(.sqlite, table, '`', .insert, true, '?', 1, data, orm.QueryData{})
52 sqlite_stmt_worker(db, query, converted_data, orm.QueryData{})!
53}
54
55// update is used internally by V's ORM for processing `UPDATE ` queries
56pub fn (db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
57 where_with_tenant := orm.apply_tenant_filter(table, where)
58 query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .update, true, '?', 1, data,
59 where_with_tenant)
60 sqlite_stmt_worker(db, query, data, where_with_tenant)!
61}
62
63// delete is used internally by V's ORM for processing `DELETE ` queries
64pub fn (db DB) delete(table orm.Table, where orm.QueryData) ! {
65 where_with_tenant := orm.apply_tenant_filter(table, where)
66 query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .delete, true, '?', 1, orm.QueryData{},
67 where_with_tenant)
68 sqlite_stmt_worker(db, query, orm.QueryData{}, where_with_tenant)!
69}
70
71// last_id is used internally by V's ORM for post-processing `INSERT ` queries
72pub fn (db DB) last_id() int {
73 query := 'SELECT last_insert_rowid();'
74
75 return db.q_int(query) or { 0 }
76}
77
78// DDL (table creation/destroying etc)
79
80// create is used internally by V's ORM for processing table creation queries (DDL)
81pub fn (db DB) create(table orm.Table, fields []orm.TableField) ! {
82 query := orm.orm_table_gen(.sqlite, table, '`', true, 0, fields, sqlite_type_from_v, false) or {
83 return err
84 }
85 sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
86}
87
88// drop is used internally by V's ORM for processing table destroying queries (DDL)
89pub fn (db DB) drop(table orm.Table) ! {
90 query := 'DROP TABLE `${table.name}`;'
91 sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
92}
93
94// execute runs a raw SQL query and returns result rows as driver-agnostic orm.Row values.
95pub fn (db DB) execute(query string) ![]orm.Row {
96 rows := db.exec(query)!
97 return rows.map(orm.Row{ vals: it.vals, names: it.names })
98}
99
100// orm_begin starts a transaction for ORM helpers.
101pub fn (mut db DB) orm_begin() ! {
102 db.begin()!
103}
104
105// orm_commit commits a transaction for ORM helpers.
106pub fn (mut db DB) orm_commit() ! {
107 db.commit()!
108}
109
110// orm_rollback rolls back a transaction for ORM helpers.
111pub fn (mut db DB) orm_rollback() ! {
112 db.rollback()!
113}
114
115// orm_savepoint creates a savepoint for ORM helpers.
116pub fn (mut db DB) orm_savepoint(name string) ! {
117 db.savepoint(name)!
118}
119
120// orm_rollback_to rolls back to a savepoint for ORM helpers.
121pub fn (mut db DB) orm_rollback_to(name string) ! {
122 db.rollback_to(name)!
123}
124
125// orm_release_savepoint releases a savepoint for ORM helpers.
126pub fn (mut db DB) orm_release_savepoint(name string) ! {
127 db.release_savepoint(name)!
128}
129
130// helper
131
132// Executes query and bind prepared statement data directly
133fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ! {
134 $if trace_sqlite ? {
135 eprintln('> sqlite_stmt_worker query: "${query}"')
136 }
137 stmt := db.new_init_stmt(query)!
138 defer {
139 stmt.finalize()
140 }
141 mut c := 1
142 sqlite_stmt_binder(stmt, data, query, mut c)!
143 sqlite_stmt_binder(stmt, where, query, mut c)!
144 stmt.orm_step(query)!
145}
146
147// Binds all values of d in the prepared statement
148fn sqlite_stmt_binder(stmt Stmt, d orm.QueryData, query string, mut c &int) ! {
149 for data in d.data {
150 err := bind(stmt, mut c, data)
151
152 if err != 0 {
153 return stmt.db.error_message(err, query)
154 }
155 if !sqlite_primitive_is_array(data) {
156 c++
157 }
158 }
159}
160
161fn sqlite_primitive_is_array(data orm.Primitive) bool {
162 return match data {
163 []orm.Primitive, []bool, []f32, []f64, []i16, []i64, []i8, []int, []string, []time.Time,
164 []u16, []u32, []u64, []u8, []orm.InfixType {
165 true
166 }
167 else {
168 false
169 }
170 }
171}
172
173fn bind_array[T](stmt Stmt, mut c &int, data []T) int {
174 mut err := 0
175 for element in data {
176 tmp_err := bind(stmt, mut c, orm.Primitive(element))
177 c++
178 if tmp_err != 0 {
179 err = tmp_err
180 break
181 }
182 }
183 return err
184}
185
186// Universal bind function
187fn bind(stmt Stmt, mut c &int, data orm.Primitive) int {
188 mut err := 0
189 match data {
190 i8, i16, int, u8, u16, u32, bool {
191 err = stmt.bind_int(c, int(data))
192 }
193 i64, u64 {
194 err = stmt.bind_i64(c, i64(data))
195 }
196 f32, f64 {
197 err = stmt.bind_f64(c, f64(data))
198 }
199 string {
200 err = stmt.bind_text(c, data)
201 }
202 time.Time {
203 err = stmt.bind_int(c, int(data.unix()))
204 }
205 orm.InfixType {
206 err = bind(stmt, mut c, data.right)
207 }
208 orm.Null {
209 err = stmt.bind_null(c)
210 }
211 []orm.Primitive {
212 err = bind_array(stmt, mut c, data)
213 }
214 []bool {
215 err = bind_array(stmt, mut c, data)
216 }
217 []f32 {
218 err = bind_array(stmt, mut c, data)
219 }
220 []f64 {
221 err = bind_array(stmt, mut c, data)
222 }
223 []i16 {
224 err = bind_array(stmt, mut c, data)
225 }
226 []i64 {
227 err = bind_array(stmt, mut c, data)
228 }
229 []i8 {
230 err = bind_array(stmt, mut c, data)
231 }
232 []int {
233 err = bind_array(stmt, mut c, data)
234 }
235 []string {
236 err = bind_array(stmt, mut c, data)
237 }
238 []time.Time {
239 err = bind_array(stmt, mut c, data)
240 }
241 []u16 {
242 err = bind_array(stmt, mut c, data)
243 }
244 []u32 {
245 err = bind_array(stmt, mut c, data)
246 }
247 []u64 {
248 err = bind_array(stmt, mut c, data)
249 }
250 []u8 {
251 err = bind_array(stmt, mut c, data)
252 }
253 []orm.InfixType {
254 err = bind_array(stmt, mut c, data)
255 }
256 }
257
258 return err
259}
260
261// Selects column in result and converts it to an orm.Primitive
262fn (stmt Stmt) sqlite_select_column(idx int, typ int) !orm.Primitive {
263 if typ in orm.nums || typ == -1 {
264 return stmt.get_int(idx) or { return orm.Null{} }
265 } else if typ in orm.num64 {
266 return stmt.get_i64(idx) or { return orm.Null{} }
267 } else if typ == typeof[f32]().idx {
268 return f32(stmt.get_f64(idx) or { return orm.Null{} })
269 } else if typ == typeof[f64]().idx {
270 return stmt.get_f64(idx) or { return orm.Null{} }
271 } else if typ == orm.type_string {
272 if v := stmt.get_text(idx) {
273 return v.clone()
274 } else {
275 return orm.Null{}
276 }
277 } else if typ == orm.enum_ {
278 return stmt.get_i64(idx) or { return orm.Null{} }
279 } else if typ == orm.time_ {
280 if v := stmt.get_int(idx) {
281 return time.unix(v)
282 } else {
283 return orm.Null{}
284 }
285 } else {
286 return error('Unknown type ${typ}')
287 }
288}
289
290// Convert type int to sql type string
291fn sqlite_type_from_v(typ int) !string {
292 return if typ in orm.nums || typ in orm.num64 || typ in [orm.serial, orm.time_, orm.enum_] {
293 'INTEGER'
294 } else if typ in orm.float {
295 'REAL'
296 } else if typ == orm.type_string {
297 'TEXT'
298 } else {
299 error('Unknown type ${typ}')
300 }
301}
302