v / vlib / db / pg / orm.v
537 lines · 486 sloc · 13.51 KB · 2b04a6ecbf16697b4a6ae2e1e02d3a381967e8f0
Raw
1module pg
2
3import orm
4import time
5import net.conv
6
7// ---- ORM on Conn (single pinned connection) ----
8
9// select is used internally by V's ORM for processing `SELECT ` queries.
10pub fn (c &Conn) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
11 c.ensure_active()!
12 where_with_tenant := orm.apply_tenant_filter(config.table, where)
13 query := orm.orm_select_gen(config, '"', true, '$', 1, where_with_tenant)
14
15 rows := pg_stmt_worker(c, query, where_with_tenant, data)!
16
17 mut ret := [][]orm.Primitive{}
18
19 for row in rows {
20 mut row_data := []orm.Primitive{}
21 for i, val in row.vals {
22 row_data << val_to_primitive(val, config.types[i])!
23 }
24 ret << row_data
25 }
26
27 return ret
28}
29
30// insert is used internally by V's ORM for processing `INSERT ` queries.
31pub fn (c &Conn) insert(table orm.Table, data orm.QueryData) ! {
32 c.ensure_active()!
33 query, converted_data :=
34 orm.orm_stmt_gen(.pg, table, '"', .insert, true, '$', 1, data, orm.QueryData{})
35 pg_stmt_worker(c, query, converted_data, orm.QueryData{})!
36}
37
38// update is used internally by V's ORM for processing `UPDATE ` queries.
39pub fn (c &Conn) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
40 c.ensure_active()!
41 where_with_tenant := orm.apply_tenant_filter(table, where)
42 query, _ := orm.orm_stmt_gen(.default, table, '"', .update, true, '$', 1, data,
43 where_with_tenant)
44 pg_stmt_worker(c, query, data, where_with_tenant)!
45}
46
47// delete is used internally by V's ORM for processing `DELETE ` queries.
48pub fn (c &Conn) delete(table orm.Table, where orm.QueryData) ! {
49 c.ensure_active()!
50 where_with_tenant := orm.apply_tenant_filter(table, where)
51 query, _ := orm.orm_stmt_gen(.default, table, '"', .delete, true, '$', 1, orm.QueryData{},
52 where_with_tenant)
53 pg_stmt_worker(c, query, orm.QueryData{}, where_with_tenant)!
54}
55
56// last_id is used internally by V's ORM for post-processing `INSERT ` queries.
57pub fn (c &Conn) last_id() int {
58 query := 'SELECT LASTVAL();'
59
60 return c.q_int(query) or { 0 }
61}
62
63// create is used internally by V's ORM for processing table creation queries (DDL).
64pub fn (c &Conn) create(table orm.Table, fields []orm.TableField) ! {
65 query := orm.orm_table_gen(.pg, table, '"', true, 0, fields, pg_type_from_v, false) or {
66 return err
67 }
68 stmts := query.split(';')
69 for stmt in stmts {
70 if stmt != '' {
71 c.exec(stmt + ';')!
72 }
73 }
74}
75
76// drop is used internally by V's ORM for processing table destroying queries (DDL).
77pub fn (c &Conn) drop(table orm.Table) ! {
78 query := 'DROP TABLE "${table.name}";'
79 c.exec(query)!
80}
81
82// orm_begin starts a transaction on this conn.
83pub fn (c &Conn) orm_begin() ! {
84 c.begin_on_conn()!
85}
86
87// orm_commit commits the transaction on this conn.
88pub fn (c &Conn) orm_commit() ! {
89 c.commit()!
90}
91
92// orm_rollback rolls back the transaction on this conn.
93pub fn (c &Conn) orm_rollback() ! {
94 c.rollback()!
95}
96
97// orm_savepoint creates a savepoint on this conn.
98pub fn (c &Conn) orm_savepoint(name string) ! {
99 c.savepoint(name)!
100}
101
102// orm_rollback_to rolls back to a savepoint on this conn.
103pub fn (c &Conn) orm_rollback_to(name string) ! {
104 c.rollback_to(name)!
105}
106
107// orm_release_savepoint releases a savepoint on this conn.
108pub fn (c &Conn) orm_release_savepoint(name string) ! {
109 c.release_savepoint(name)!
110}
111
112// ---- ORM on DB (acquire-use-release per call) ----
113
114// select acquires a conn from the pool and runs the ORM SELECT on it.
115pub fn (mut db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
116 mut c := db.pool.acquire()!
117 defer {
118 c.close() or {}
119 }
120 return c.select(config, data, where)
121}
122
123// insert acquires a conn from the pool, runs the ORM INSERT on it, and
124// stashes LASTVAL() captured on the same session for the calling thread.
125// V's `sql db { insert ... }` macro emits a follow-up `db.last_id()` call;
126// stashing here lets that read return the correct id even though the pool
127// may hand out a different conn for the second call.
128pub fn (mut db DB) insert(table orm.Table, data orm.QueryData) ! {
129 mut c := db.pool.acquire()!
130 defer {
131 c.close() or {}
132 }
133 c.insert(table, data)!
134 db.pool.stash_last_id(c.last_id())
135}
136
137// update acquires a conn from the pool and runs the ORM UPDATE on it.
138pub fn (mut db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
139 mut c := db.pool.acquire()!
140 defer {
141 c.close() or {}
142 }
143 c.update(table, data, where)!
144}
145
146// delete acquires a conn from the pool and runs the ORM DELETE on it.
147pub fn (mut db DB) delete(table orm.Table, where orm.QueryData) ! {
148 mut c := db.pool.acquire()!
149 defer {
150 c.close() or {}
151 }
152 c.delete(table, where)!
153}
154
155// create acquires a conn from the pool and runs CREATE TABLE on it.
156pub fn (mut db DB) create(table orm.Table, fields []orm.TableField) ! {
157 mut c := db.pool.acquire()!
158 defer {
159 c.close() or {}
160 }
161 c.create(table, fields)!
162}
163
164// drop acquires a conn from the pool and runs DROP TABLE on it.
165pub fn (mut db DB) drop(table orm.Table) ! {
166 mut c := db.pool.acquire()!
167 defer {
168 c.close() or {}
169 }
170 c.drop(table)!
171}
172
173// last_id returns the id stashed by this thread's most recent `DB.insert`
174// (or 0 if there is none). LASTVAL() itself is session-scoped, so calling
175// it on a freshly-checked-out pool conn would return the wrong value or 0;
176// `DB.insert` captures it on the same conn that ran the INSERT and stashes
177// it per-thread, which is what V's ORM macro expects.
178pub fn (mut db DB) last_id() int {
179 return db.pool.take_last_id()
180}
181
182// ---- ORM on Tx (use the pinned conn) ----
183
184// select runs the ORM SELECT on the pinned transaction conn.
185pub fn (mut tx Tx) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
186 tx.ensure_active()!
187 return tx.conn.select(config, data, where)
188}
189
190// insert runs the ORM INSERT on the pinned transaction conn.
191pub fn (mut tx Tx) insert(table orm.Table, data orm.QueryData) ! {
192 tx.ensure_active()!
193 tx.conn.insert(table, data)!
194}
195
196// update runs the ORM UPDATE on the pinned transaction conn.
197pub fn (mut tx Tx) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
198 tx.ensure_active()!
199 tx.conn.update(table, data, where)!
200}
201
202// delete runs the ORM DELETE on the pinned transaction conn.
203pub fn (mut tx Tx) delete(table orm.Table, where orm.QueryData) ! {
204 tx.ensure_active()!
205 tx.conn.delete(table, where)!
206}
207
208// create runs CREATE TABLE on the pinned transaction conn.
209pub fn (mut tx Tx) create(table orm.Table, fields []orm.TableField) ! {
210 tx.ensure_active()!
211 tx.conn.create(table, fields)!
212}
213
214// drop runs DROP TABLE on the pinned transaction conn.
215pub fn (mut tx Tx) drop(table orm.Table) ! {
216 tx.ensure_active()!
217 tx.conn.drop(table)!
218}
219
220// last_id returns the last inserted id on the pinned conn.
221pub fn (mut tx Tx) last_id() int {
222 if tx.done || isnil(tx.conn) {
223 return 0
224 }
225 return tx.conn.last_id()
226}
227
228// orm_begin is a no-op on Tx (begin already ran when the Tx was created).
229// It exists so Tx satisfies `orm.TransactionalConnection` for nested savepoints.
230pub fn (mut tx Tx) orm_begin() ! {
231}
232
233// orm_commit commits the Tx.
234pub fn (mut tx Tx) orm_commit() ! {
235 tx.commit()!
236}
237
238// orm_rollback rolls back the Tx.
239pub fn (mut tx Tx) orm_rollback() ! {
240 tx.rollback()!
241}
242
243// orm_savepoint creates a savepoint inside the Tx.
244pub fn (mut tx Tx) orm_savepoint(name string) ! {
245 tx.savepoint(name)!
246}
247
248// orm_rollback_to rolls back to a savepoint inside the Tx.
249pub fn (mut tx Tx) orm_rollback_to(name string) ! {
250 tx.rollback_to(name)!
251}
252
253// orm_release_savepoint releases a savepoint inside the Tx.
254pub fn (mut tx Tx) orm_release_savepoint(name string) ! {
255 tx.release_savepoint(name)!
256}
257
258// ---- utils ----
259
260fn pg_stmt_binder(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, d orm.QueryData) {
261 for data in d.data {
262 pg_stmt_match(mut types, mut vals, mut lens, mut formats, data)
263 }
264}
265
266fn pg_stmt_match_array[T](mut types []u32, mut vals []&char, mut lens []int, mut formats []int, data []T) {
267 for element in data {
268 pg_stmt_match(mut types, mut vals, mut lens, mut formats, orm.Primitive(element))
269 }
270}
271
272fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, data orm.Primitive) {
273 match data {
274 bool {
275 types << u32(Oid.t_bool)
276 vals << &char(&data)
277 lens << int(sizeof(bool))
278 formats << 1
279 }
280 u8 {
281 types << u32(Oid.t_char)
282 vals << &char(&data)
283 lens << int(sizeof(u8))
284 formats << 1
285 }
286 u16 {
287 types << u32(Oid.t_int2)
288 num := conv.hton16(data)
289 vals << &char(&num)
290 lens << int(sizeof(u16))
291 formats << 1
292 }
293 u32 {
294 types << u32(Oid.t_int4)
295 num := conv.hton32(data)
296 vals << &char(&num)
297 lens << int(sizeof(u32))
298 formats << 1
299 }
300 u64 {
301 types << u32(Oid.t_int8)
302 num := conv.hton64(data)
303 vals << &char(&num)
304 lens << int(sizeof(u64))
305 formats << 1
306 }
307 i8 {
308 types << u32(Oid.t_char)
309 vals << &char(&data)
310 lens << int(sizeof(i8))
311 formats << 1
312 }
313 i16 {
314 types << u32(Oid.t_int2)
315 num := conv.hton16(u16(data))
316 vals << &char(&num)
317 lens << int(sizeof(i16))
318 formats << 1
319 }
320 int {
321 types << u32(Oid.t_int4)
322 num := conv.hton32(u32(data))
323 vals << &char(&num)
324 lens << int(sizeof(int))
325 formats << 1
326 }
327 i64 {
328 types << u32(Oid.t_int8)
329 num := conv.hton64(u64(data))
330 vals << &char(&num)
331 lens << int(sizeof(i64))
332 formats << 1
333 }
334 f32 {
335 types << u32(Oid.t_float4)
336 num := conv.htonf32(f32(data))
337 vals << &char(&num)
338 lens << int(sizeof(f32))
339 formats << 1
340 }
341 f64 {
342 types << u32(Oid.t_float8)
343 num := conv.htonf64(f64(data))
344 vals << &char(&num)
345 lens << int(sizeof(f64))
346 formats << 1
347 }
348 string {
349 // If paramTypes is NULL, or any particular element in the array is zero,
350 // the server infers a data type for the parameter symbol in the same way
351 // it would do for an untyped literal string.
352 types << u32(0)
353 vals << &char(data.str)
354 lens << data.len
355 formats << 0
356 }
357 time.Time {
358 datetime := data.format_ss()
359 types << u32(0)
360 vals << &char(datetime.str)
361 lens << datetime.len
362 formats << 0
363 }
364 orm.InfixType {
365 pg_stmt_match(mut types, mut vals, mut lens, mut formats, data.right)
366 }
367 orm.Null {
368 types << u32(0) // we do not know col type, let server infer
369 vals << &char(unsafe { nil }) // NULL pointer indicates NULL
370 lens << int(0) // ignored
371 formats << 0 // ignored
372 }
373 []orm.Primitive {
374 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
375 }
376 []bool {
377 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
378 }
379 []f32 {
380 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
381 }
382 []f64 {
383 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
384 }
385 []i16 {
386 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
387 }
388 []i64 {
389 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
390 }
391 []i8 {
392 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
393 }
394 []int {
395 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
396 }
397 []string {
398 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
399 }
400 []time.Time {
401 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
402 }
403 []u16 {
404 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
405 }
406 []u32 {
407 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
408 }
409 []u64 {
410 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
411 }
412 []u8 {
413 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
414 }
415 []orm.InfixType {
416 pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
417 }
418 }
419}
420
421fn pg_type_from_v(typ int) !string {
422 str := match typ {
423 orm.type_idx['i8'], orm.type_idx['i16'], orm.type_idx['u8'], orm.type_idx['u16'] {
424 'SMALLINT'
425 }
426 orm.type_idx['bool'] {
427 'BOOLEAN'
428 }
429 orm.type_idx['int'], orm.type_idx['u32'] {
430 'INT'
431 }
432 orm.time_ {
433 'TIMESTAMP'
434 }
435 orm.enum_ {
436 'BIGINT'
437 }
438 orm.type_idx['i64'], orm.type_idx['u64'] {
439 'BIGINT'
440 }
441 orm.float[0] {
442 'REAL'
443 }
444 orm.float[1] {
445 'DOUBLE PRECISION'
446 }
447 orm.type_string {
448 'TEXT'
449 }
450 orm.serial {
451 'SERIAL'
452 }
453 else {
454 ''
455 }
456 }
457
458 if str == '' {
459 return error('Unknown type ${typ}')
460 }
461 return str
462}
463
464fn val_to_primitive(val ?string, typ int) !orm.Primitive {
465 if str := val {
466 match typ {
467 // bool
468 orm.type_idx['bool'] {
469 return orm.Primitive(str == 't')
470 }
471 // i8
472 orm.type_idx['i8'] {
473 return orm.Primitive(str.i8())
474 }
475 // i16
476 orm.type_idx['i16'] {
477 return orm.Primitive(str.i16())
478 }
479 // int
480 orm.type_idx['int'] {
481 return orm.Primitive(str.int())
482 }
483 // i64
484 orm.type_idx['i64'] {
485 return orm.Primitive(str.i64())
486 }
487 // u8
488 orm.type_idx['u8'] {
489 data := str.i8()
490 return orm.Primitive(*unsafe { &u8(&data) })
491 }
492 // u16
493 orm.type_idx['u16'] {
494 data := str.i16()
495 return orm.Primitive(*unsafe { &u16(&data) })
496 }
497 // u32
498 orm.type_idx['u32'] {
499 data := str.int()
500 return orm.Primitive(*unsafe { &u32(&data) })
501 }
502 // u64
503 orm.type_idx['u64'] {
504 data := str.i64()
505 return orm.Primitive(*unsafe { &u64(&data) })
506 }
507 // f32
508 orm.type_idx['f32'] {
509 return orm.Primitive(str.f32())
510 }
511 // f64
512 orm.type_idx['f64'] {
513 return orm.Primitive(str.f64())
514 }
515 orm.type_string {
516 return orm.Primitive(str)
517 }
518 orm.time_ {
519 if str.contains_any(' /:-') {
520 date_time_str := time.parse(str)!
521 return orm.Primitive(date_time_str)
522 }
523
524 timestamp := str.int()
525 return orm.Primitive(time.unix(timestamp))
526 }
527 orm.enum_ {
528 return orm.Primitive(str.i64())
529 }
530 else {}
531 }
532
533 return error('Unknown field type ${typ}')
534 } else {
535 return orm.Null{}
536 }
537}
538