v / vlib / db / sqlite / orm.v
295 lines · 267 sloc · 7.39 KB · 8288cbb985e955454fc2bc52abbc0b43db682c0f
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// orm_begin starts a transaction for ORM helpers.
95pub fn (mut db DB) orm_begin() ! {
96 db.begin()!
97}
98
99// orm_commit commits a transaction for ORM helpers.
100pub fn (mut db DB) orm_commit() ! {
101 db.commit()!
102}
103
104// orm_rollback rolls back a transaction for ORM helpers.
105pub fn (mut db DB) orm_rollback() ! {
106 db.rollback()!
107}
108
109// orm_savepoint creates a savepoint for ORM helpers.
110pub fn (mut db DB) orm_savepoint(name string) ! {
111 db.savepoint(name)!
112}
113
114// orm_rollback_to rolls back to a savepoint for ORM helpers.
115pub fn (mut db DB) orm_rollback_to(name string) ! {
116 db.rollback_to(name)!
117}
118
119// orm_release_savepoint releases a savepoint for ORM helpers.
120pub fn (mut db DB) orm_release_savepoint(name string) ! {
121 db.release_savepoint(name)!
122}
123
124// helper
125
126// Executes query and bind prepared statement data directly
127fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ! {
128 $if trace_sqlite ? {
129 eprintln('> sqlite_stmt_worker query: "${query}"')
130 }
131 stmt := db.new_init_stmt(query)!
132 defer {
133 stmt.finalize()
134 }
135 mut c := 1
136 sqlite_stmt_binder(stmt, data, query, mut c)!
137 sqlite_stmt_binder(stmt, where, query, mut c)!
138 stmt.orm_step(query)!
139}
140
141// Binds all values of d in the prepared statement
142fn sqlite_stmt_binder(stmt Stmt, d orm.QueryData, query string, mut c &int) ! {
143 for data in d.data {
144 err := bind(stmt, mut c, data)
145
146 if err != 0 {
147 return stmt.db.error_message(err, query)
148 }
149 if !sqlite_primitive_is_array(data) {
150 c++
151 }
152 }
153}
154
155fn sqlite_primitive_is_array(data orm.Primitive) bool {
156 return match data {
157 []orm.Primitive, []bool, []f32, []f64, []i16, []i64, []i8, []int, []string, []time.Time,
158 []u16, []u32, []u64, []u8, []orm.InfixType {
159 true
160 }
161 else {
162 false
163 }
164 }
165}
166
167fn bind_array[T](stmt Stmt, mut c &int, data []T) int {
168 mut err := 0
169 for element in data {
170 tmp_err := bind(stmt, mut c, orm.Primitive(element))
171 c++
172 if tmp_err != 0 {
173 err = tmp_err
174 break
175 }
176 }
177 return err
178}
179
180// Universal bind function
181fn bind(stmt Stmt, mut c &int, data orm.Primitive) int {
182 mut err := 0
183 match data {
184 i8, i16, int, u8, u16, u32, bool {
185 err = stmt.bind_int(c, int(data))
186 }
187 i64, u64 {
188 err = stmt.bind_i64(c, i64(data))
189 }
190 f32, f64 {
191 err = stmt.bind_f64(c, f64(data))
192 }
193 string {
194 err = stmt.bind_text(c, data)
195 }
196 time.Time {
197 err = stmt.bind_int(c, int(data.unix()))
198 }
199 orm.InfixType {
200 err = bind(stmt, mut c, data.right)
201 }
202 orm.Null {
203 err = stmt.bind_null(c)
204 }
205 []orm.Primitive {
206 err = bind_array(stmt, mut c, data)
207 }
208 []bool {
209 err = bind_array(stmt, mut c, data)
210 }
211 []f32 {
212 err = bind_array(stmt, mut c, data)
213 }
214 []f64 {
215 err = bind_array(stmt, mut c, data)
216 }
217 []i16 {
218 err = bind_array(stmt, mut c, data)
219 }
220 []i64 {
221 err = bind_array(stmt, mut c, data)
222 }
223 []i8 {
224 err = bind_array(stmt, mut c, data)
225 }
226 []int {
227 err = bind_array(stmt, mut c, data)
228 }
229 []string {
230 err = bind_array(stmt, mut c, data)
231 }
232 []time.Time {
233 err = bind_array(stmt, mut c, data)
234 }
235 []u16 {
236 err = bind_array(stmt, mut c, data)
237 }
238 []u32 {
239 err = bind_array(stmt, mut c, data)
240 }
241 []u64 {
242 err = bind_array(stmt, mut c, data)
243 }
244 []u8 {
245 err = bind_array(stmt, mut c, data)
246 }
247 []orm.InfixType {
248 err = bind_array(stmt, mut c, data)
249 }
250 }
251
252 return err
253}
254
255// Selects column in result and converts it to an orm.Primitive
256fn (stmt Stmt) sqlite_select_column(idx int, typ int) !orm.Primitive {
257 if typ in orm.nums || typ == -1 {
258 return stmt.get_int(idx) or { return orm.Null{} }
259 } else if typ in orm.num64 {
260 return stmt.get_i64(idx) or { return orm.Null{} }
261 } else if typ == typeof[f32]().idx {
262 return f32(stmt.get_f64(idx) or { return orm.Null{} })
263 } else if typ == typeof[f64]().idx {
264 return stmt.get_f64(idx) or { return orm.Null{} }
265 } else if typ == orm.type_string {
266 if v := stmt.get_text(idx) {
267 return v.clone()
268 } else {
269 return orm.Null{}
270 }
271 } else if typ == orm.enum_ {
272 return stmt.get_i64(idx) or { return orm.Null{} }
273 } else if typ == orm.time_ {
274 if v := stmt.get_int(idx) {
275 return time.unix(v)
276 } else {
277 return orm.Null{}
278 }
279 } else {
280 return error('Unknown type ${typ}')
281 }
282}
283
284// Convert type int to sql type string
285fn sqlite_type_from_v(typ int) !string {
286 return if typ in orm.nums || typ in orm.num64 || typ in [orm.serial, orm.time_, orm.enum_] {
287 'INTEGER'
288 } else if typ in orm.float {
289 'REAL'
290 } else if typ == orm.type_string {
291 'TEXT'
292 } else {
293 error('Unknown type ${typ}')
294 }
295}
296