v2 / vlib / db / sqlite / sqlite.c.v
669 lines · 593 sloc · 17.97 KB · bbecb3eeeb122074bc23eee2ac11c8fa0657e08b
Raw
1module sqlite
2
3$if freebsd || openbsd {
4 #flag -I/usr/local/include
5 #flag -L/usr/local/lib
6}
7$if tinyc {
8 #flag -DSQLITE_DISABLE_INTRINSIC
9}
10$if $pkgconfig('sqlite3') {
11 #pkgconfig sqlite3
12 #include "sqlite3.h" # The SQLite header file is missing. Please install the corresponding development package.
13} $else $if windows {
14 #flag -I@VEXEROOT/thirdparty/sqlite
15 #flag @VEXEROOT/thirdparty/sqlite/sqlite3.o
16 #include "sqlite3.h" # The SQLite header file is missing. Please run vlib/db/sqlite/install_thirdparty_sqlite.vsh to download an SQLite amalgamation.
17} $else $if darwin {
18 // macOS ships libsqlite3, so do not require a separately downloaded amalgamation.
19 #flag darwin -lsqlite3
20} $else {
21 #flag -I@VEXEROOT/thirdparty/sqlite
22 #include "sqlite3.h" # The SQLite header file is missing. Please run vlib/db/sqlite/install_thirdparty_sqlite.vsh to download an SQLite amalgamation.
23 #flag @VEXEROOT/thirdparty/sqlite/sqlite3.c
24}
25
26// https://www.sqlite.org/rescode.html
27pub const sqlite_ok = 0
28pub const sqlite_error = 1
29pub const sqlite_row = 100
30pub const sqlite_done = 101
31pub const sqlite_cantopen = 14
32pub const sqlite_ioerr_read = 266
33pub const sqlite_ioerr_short_read = 522
34pub const sqlite_ioerr_write = 778
35pub const sqlite_ioerr_fsync = 1034
36pub const sqlite_ioerr_fstat = 1802
37pub const sqlite_ioerr_delete = 2570
38
39pub const sqlite_open_main_db = 0x00000100
40pub const sqlite_open_temp_db = 0x00000200
41pub const sqlite_open_transient_db = 0x00000400
42pub const sqlite_open_main_journal = 0x00000800
43pub const sqlite_open_temp_journal = 0x00001000
44pub const sqlite_open_subjournal = 0x00002000
45pub const sqlite_open_super_journal = 0x00004000
46pub const sqlite_open_wal = 0x00080000
47
48pub enum SyncMode {
49 off
50 normal
51 full
52}
53
54pub enum Sqlite3TransactionLevel {
55 deferred
56 immediate
57 exclusive
58}
59
60pub enum JournalMode {
61 off
62 delete
63 truncate
64 persist
65 memory
66}
67
68pub struct C.sqlite3 {
69}
70
71pub struct C.sqlite3_stmt {
72}
73
74@[heap]
75pub struct Stmt {
76 stmt &C.sqlite3_stmt = unsafe { nil }
77 db &DB = unsafe { nil }
78}
79
80struct SQLError {
81 MessageError
82}
83
84//
85@[heap]
86pub struct DB {
87pub mut:
88 is_open bool
89mut:
90 conn &C.sqlite3 = unsafe { nil }
91}
92
93// str returns a text representation of the DB
94pub fn (db &DB) str() string {
95 return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }'
96}
97
98pub struct Row {
99pub mut:
100 vals []string
101 names []string
102}
103
104// val returns the value at `index`.
105pub fn (row Row) val(index int) string {
106 return row.vals[index]
107}
108
109// values returns all row values.
110pub fn (row Row) values() []string {
111 return row.vals.clone()
112}
113
114// get_string returns the value for the given column name, or '' if the column is not found
115// or if the corresponding value index is out of range.
116pub fn (r &Row) get_string(col_name string) string {
117 for i, name in r.names {
118 if name == col_name {
119 if i < r.vals.len {
120 return r.vals[i]
121 }
122 return ''
123 }
124 }
125 return ''
126}
127
128// get_int returns the integer value for the given column name, or 0 if the column is not found.
129pub fn (r &Row) get_int(col_name string) int {
130 return r.get_string(col_name).int()
131}
132
133pub type Params = []string | [][]string
134
135fn sqlite_cstring(s string) &char {
136 cstr := unsafe { malloc_noscan(s.len + 1) }
137 unsafe {
138 if s.len > 0 {
139 vmemcpy(cstr, s.str, s.len)
140 }
141 cstr[s.len] = 0
142 }
143 return &char(cstr)
144}
145
146//
147fn C.sqlite3_open(&char, &&C.sqlite3) i32
148
149fn C.sqlite3_close(&C.sqlite3) i32
150
151fn C.sqlite3_busy_timeout(db &C.sqlite3, ms i32) i32
152
153fn C.sqlite3_last_insert_rowid(&C.sqlite3) i64
154
155//
156fn C.sqlite3_prepare_v2(&C.sqlite3, &char, i32, &&C.sqlite3_stmt, &&char) i32
157
158fn C.sqlite3_step(&C.sqlite3_stmt) i32
159
160fn C.sqlite3_reset(&C.sqlite3_stmt) i32
161
162fn C.sqlite3_finalize(&C.sqlite3_stmt) i32
163
164//
165fn C.sqlite3_column_name(&C.sqlite3_stmt, i32) &char
166
167fn C.sqlite3_column_text(&C.sqlite3_stmt, i32) &u8
168
169fn C.sqlite3_column_int(&C.sqlite3_stmt, i32) i32
170
171fn C.sqlite3_column_int64(&C.sqlite3_stmt, i32) i64
172
173fn C.sqlite3_column_double(&C.sqlite3_stmt, i32) f64
174
175fn C.sqlite3_column_count(&C.sqlite3_stmt) i32
176
177fn C.sqlite3_column_type(&C.sqlite3_stmt, i32) i32
178
179fn C.sqlite3_column_bytes(&C.sqlite3_stmt, i32) i32
180
181//
182fn C.sqlite3_errstr(i32) &char
183
184fn C.sqlite3_errmsg(&C.sqlite3) &char
185
186fn C.sqlite3_free(voidptr)
187
188fn C.sqlite3_changes(&C.sqlite3) i32
189
190// connect Opens the connection with a database.
191pub fn connect(path string) !DB {
192 db := &C.sqlite3(unsafe { nil })
193 path_cstr := sqlite_cstring(path)
194 defer {
195 unsafe { free(path_cstr) }
196 }
197 code := C.sqlite3_open(path_cstr, &db)
198 if code != 0 {
199 return &SQLError{
200 msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db))) }
201 code: code
202 }
203 }
204 return DB{
205 conn: db
206 is_open: true
207 }
208}
209
210// close Closes the DB.
211// TODO: For all functions, determine whether the connection is
212// closed first, and determine what to do if it is
213pub fn (mut db DB) close() ! {
214 code := C.sqlite3_close(db.conn)
215 if code == 0 {
216 db.is_open = false
217 } else {
218 return &SQLError{
219 msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
220 code: code
221 }
222 }
223}
224
225// Only for V ORM
226fn get_int_from_stmt(stmt &C.sqlite3_stmt) int {
227 x := C.sqlite3_step(stmt)
228 if x != C.SQLITE_OK && x != C.SQLITE_DONE {
229 C.puts(C.sqlite3_errstr(x))
230 }
231 res := C.sqlite3_column_int(stmt, 0)
232 C.sqlite3_finalize(stmt)
233 return res
234}
235
236// last_insert_rowid returns last inserted rowid
237// https://www.sqlite.org/c3ref/last_insert_rowid.html
238pub fn (db &DB) last_insert_rowid() i64 {
239 return C.sqlite3_last_insert_rowid(db.conn)
240}
241
242// get_affected_rows_count returns `sqlite changes()` meaning amount of rows affected by most recent sql query
243pub fn (db &DB) get_affected_rows_count() int {
244 return C.sqlite3_changes(db.conn)
245}
246
247// q_int returns a single integer value, from the first column of the result of executing `query`, or an error on failure
248pub fn (db &DB) q_int(query string) !int {
249 $if trace_sqlite ? {
250 eprintln('> q_int query: "${query}"')
251 }
252 stmt := &C.sqlite3_stmt(unsafe { nil })
253 pres := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
254 if pres != sqlite_ok {
255 return db.error_message(pres, query)
256 }
257 defer {
258 C.sqlite3_finalize(stmt)
259 }
260 code := C.sqlite3_step(stmt)
261 if code != sqlite_row {
262 return db.error_message(code, query)
263 }
264 res := C.sqlite3_column_int(stmt, 0)
265 return res
266}
267
268// q_string returns a single string value, from the first column of the result of executing `query`, or an error on failure
269pub fn (db &DB) q_string(query string) !string {
270 $if trace_sqlite ? {
271 eprintln('> q_string query: "${query}"')
272 }
273 stmt := &C.sqlite3_stmt(unsafe { nil })
274 pres := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
275 if pres != sqlite_ok {
276 return db.error_message(pres, query)
277 }
278 defer {
279 C.sqlite3_finalize(stmt)
280 }
281 code := C.sqlite3_step(stmt)
282 if code != sqlite_row {
283 return db.error_message(code, query)
284 }
285 val := unsafe { &u8(C.sqlite3_column_text(stmt, 0)) }
286 return if val != &u8(unsafe { nil }) { unsafe { tos_clone(val) } } else { '' }
287}
288
289// exec_map executes the query on the given `db`, and returns an array of maps of strings, or an error on failure
290pub fn (db &DB) exec_map(query string) ![]map[string]string {
291 $if trace_sqlite ? {
292 eprintln('> exec_map query: "${query}"')
293 }
294 stmt := &C.sqlite3_stmt(unsafe { nil })
295 mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
296 if code != sqlite_ok {
297 return db.error_message(code, query)
298 }
299 defer {
300 C.sqlite3_finalize(stmt)
301 }
302 nr_cols := C.sqlite3_column_count(stmt)
303 mut col_names := []string{cap: nr_cols}
304 for i in 0 .. nr_cols {
305 col_char := unsafe { &u8(C.sqlite3_column_name(stmt, i)) }
306 col_names << if col_char != unsafe { nil } { unsafe { tos_clone(col_char) } } else { '' }
307 }
308 mut res := 0
309 mut rows := []map[string]string{}
310 for {
311 res = C.sqlite3_step(stmt)
312 if res != sqlite_row {
313 break
314 }
315 mut row := map[string]string{}
316 for i in 0 .. nr_cols {
317 val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
318 row[col_names[i]] = if val != unsafe { nil } { unsafe { tos_clone(val) } } else { '' }
319 }
320 rows << row
321 }
322 return rows
323}
324
325fn C.sqlite3_memory_used() i64
326
327// exec executes the query on the given `db`, and returns an array of all the results, or an error on failure
328pub fn (db &DB) exec(query string) ![]Row {
329 $if trace_sqlite ? {
330 eprintln('> exec query: "${query}"')
331 }
332 stmt := &C.sqlite3_stmt(unsafe { nil })
333 mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
334 if code != sqlite_ok {
335 return db.error_message(code, query)
336 }
337 defer {
338 C.sqlite3_finalize(stmt)
339 }
340 nr_cols := C.sqlite3_column_count(stmt)
341 mut col_names := []string{cap: nr_cols}
342 for i in 0 .. nr_cols {
343 col_char := unsafe { &u8(C.sqlite3_column_name(stmt, i)) }
344 col_names << if col_char != unsafe { nil } { unsafe { tos_clone(col_char) } } else { '' }
345 }
346 mut res := 0
347 mut rows := []Row{}
348 for {
349 res = C.sqlite3_step(stmt)
350 if res != sqlite_row {
351 break
352 }
353 mut row := Row{
354 names: col_names
355 }
356 for i in 0 .. nr_cols {
357 val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
358 if val == &u8(unsafe { nil }) {
359 row.vals << ''
360 } else {
361 row.vals << unsafe { tos_clone(val) }
362 }
363 }
364 rows << row
365 }
366 return rows
367}
368
369// exec_one executes a query on the given `db`.
370// It returns either the first row from the result, if the query was successful, or an error.
371@[manualfree]
372pub fn (db &DB) exec_one(query string) !Row {
373 rows := db.exec(query)!
374 defer {
375 unsafe { rows.free() }
376 }
377 if rows.len == 0 {
378 return &SQLError{
379 msg: 'No rows'
380 code: sqlite_done
381 }
382 }
383 res := rows[0]
384 return res
385}
386
387// error_message returns a proper V error, given an integer error code received from SQLite, and a query string
388@[manualfree]
389pub fn (db &DB) error_message(code int, query string) IError {
390 errmsg := unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
391 msg := '${errmsg} (${code}) (${query})'
392 unsafe { errmsg.free() }
393 return SQLError{
394 msg: msg
395 code: code
396 }
397}
398
399// exec_none executes a query, and returns the integer SQLite result code.
400// Use it, in case you don't expect any row results, but still want a result code.
401// e.g. for queries like these: `INSERT INTO ... VALUES (...)`
402pub fn (db &DB) exec_none(query string) int {
403 $if trace_sqlite ? {
404 eprintln('> exec_none query: "${query}"')
405 }
406 stmt := &C.sqlite3_stmt(unsafe { nil })
407 pres := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
408 if pres != sqlite_ok {
409 return -1
410 }
411 defer {
412 C.sqlite3_finalize(stmt)
413 }
414 code := C.sqlite3_step(stmt)
415 return code
416}
417
418// exec_param_many executes a query with parameters provided as ?,
419// and returns either an error on failure, or the full result set on success
420pub fn (db &DB) exec_param_many(query string, params Params) ![]Row {
421 $if trace_sqlite ? {
422 eprintln('> exec_param_many query: "${query}", params: ${params}')
423 }
424 mut stmt := &C.sqlite3_stmt(unsafe { nil })
425 mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), -1, &stmt, 0)
426 if code != sqlite_ok {
427 return db.error_message(code, query)
428 }
429 defer {
430 C.sqlite3_finalize(stmt)
431 }
432
433 mut rows := []Row{}
434 nr_cols := C.sqlite3_column_count(stmt)
435 mut col_names := []string{cap: nr_cols}
436 for i in 0 .. nr_cols {
437 col_char := unsafe { &u8(C.sqlite3_column_name(stmt, i)) }
438 col_names << if col_char != unsafe { nil } { unsafe { tos_clone(col_char) } } else { '' }
439 }
440
441 if params is []string {
442 for i, param in params {
443 code = C.sqlite3_bind_text(stmt, i + 1, voidptr(param.str), param.len, 0)
444 if code != sqlite_ok {
445 return db.error_message(code, query)
446 }
447 }
448 for {
449 mut row := Row{
450 names: col_names
451 }
452 code = C.sqlite3_step(stmt)
453 if is_error(code) {
454 return db.error_message(code, query)
455 }
456 if code == sqlite_done {
457 break
458 }
459 for i in 0 .. nr_cols {
460 val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
461 if val == &u8(unsafe { nil }) {
462 row.vals << ''
463 } else {
464 row.vals << unsafe { tos_clone(val) }
465 }
466 }
467 rows << row
468 }
469 } else if params is [][]string {
470 // Rows to process
471 for params_row in params {
472 mut row := Row{
473 names: col_names
474 }
475 // Param values to bind
476 for i, param in params_row {
477 code = C.sqlite3_bind_text(stmt, i + 1, voidptr(param.str), param.len, 0)
478 if code != sqlite_ok {
479 return db.error_message(code, query)
480 }
481 }
482 for {
483 code = C.sqlite3_step(stmt)
484 if is_error(code) {
485 return db.error_message(code, query)
486 }
487 if code == sqlite_done {
488 break
489 }
490 for i in 0 .. nr_cols {
491 val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
492 if val == &u8(unsafe { nil }) {
493 row.vals << ''
494 } else {
495 row.vals << unsafe { tos_clone(val) }
496 }
497 }
498 rows << row
499 }
500 C.sqlite3_reset(stmt)
501 }
502 }
503
504 return rows
505}
506
507// exec_param executes a query with one parameter provided as a ?,
508// and returns either an error on failure, or the full result set on success
509pub fn (db &DB) exec_param(query string, param string) ![]Row {
510 return db.exec_param_many(query, [param])
511}
512
513// exec_param2 executes a query with two parameters provided as ? placeholders.
514pub fn (db &DB) exec_param2(query string, param string, param2 string) ![]Row {
515 return db.exec_param_many(query, [param, param2])
516}
517
518// create_table issues a "create table if not exists" command to the db.
519// It creates the table named 'table_name', with columns generated from 'columns' array.
520// The default columns type will be TEXT.
521pub fn (mut db DB) create_table(table_name string, columns []string) ! {
522 db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')!
523}
524
525// busy_timeout sets a busy timeout in milliseconds.
526// Sleeps for a specified amount of time when a table is locked. The handler
527// will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
528// (see https://www.sqlite.org/c3ref/busy_timeout.html)
529pub fn (db &DB) busy_timeout(ms int) int {
530 return C.sqlite3_busy_timeout(db.conn, ms)
531}
532
533// synchronization_mode sets disk synchronization mode, which controls how
534// aggressively SQLite will write data to physical storage.
535// If the command fails to execute an error is returned
536// .off: No syncs at all. (fastest)
537// .normal: Sync after each sequence of critical disk operations.
538// .full: Sync after each critical disk operation (slowest).
539pub fn (db &DB) synchronization_mode(sync_mode SyncMode) ! {
540 if sync_mode == .off {
541 db.exec('pragma synchronous = OFF;')!
542 } else if sync_mode == .full {
543 db.exec('pragma synchronous = FULL;')!
544 } else {
545 db.exec('pragma synchronous = NORMAL;')!
546 }
547}
548
549// journal_mode controls how the journal file is stored and processed.
550// If the command fails to execute an error is returned
551// .off: No journal record is kept. (fastest)
552// .memory: Journal record is held in memory, rather than on disk.
553// .delete: At the conclusion of a transaction, journal file is deleted.
554// .truncate: Journal file is truncated to a length of zero bytes.
555// .persist: Journal file is left in place, but the header is overwritten to indicate journal is no longer valid.
556pub fn (db &DB) journal_mode(journal_mode JournalMode) ! {
557 if journal_mode == .off {
558 db.exec('pragma journal_mode = OFF;')!
559 } else if journal_mode == .delete {
560 db.exec('pragma journal_mode = DELETE;')!
561 } else if journal_mode == .truncate {
562 db.exec('pragma journal_mode = TRUNCATE;')!
563 } else if journal_mode == .persist {
564 db.exec('pragma journal_mode = PERSIST;')!
565 } else if journal_mode == .memory {
566 db.exec('pragma journal_mode = MEMORY;')!
567 } else {
568 db.exec('pragma journal_mode = MEMORY;')!
569 }
570}
571
572@[params]
573pub struct Sqlite3TransactionParam {
574 transaction_level Sqlite3TransactionLevel = .deferred
575}
576
577// begin begins a new transaction.
578pub fn (mut db DB) begin(param Sqlite3TransactionParam) ! {
579 mut sql_stmt := 'BEGIN '
580 match param.transaction_level {
581 .deferred { sql_stmt += 'DEFERRED;' }
582 .immediate { sql_stmt += 'IMMEDIATE;' }
583 .exclusive { sql_stmt += 'EXCLUSIVE;' }
584 }
585
586 db.exec(sql_stmt)!
587}
588
589// savepoint create a new savepoint.
590pub fn (mut db DB) savepoint(savepoint string) ! {
591 if !savepoint.is_identifier() {
592 return error('savepoint should be a identifier string')
593 }
594 db.exec('SAVEPOINT ${savepoint};')!
595}
596
597// commit commits the current transaction.
598pub fn (mut db DB) commit() ! {
599 db.exec('COMMIT;')!
600}
601
602// rollback rollbacks the current transaction.
603pub fn (mut db DB) rollback() ! {
604 db.exec('ROLLBACK;')!
605}
606
607// rollback_to rollbacks to a specified savepoint.
608pub fn (mut db DB) rollback_to(savepoint string) ! {
609 if !savepoint.is_identifier() {
610 return error('savepoint should be a identifier string')
611 }
612 db.exec('ROLLBACK TO ${savepoint};')!
613}
614
615// release_savepoint releases a specified savepoint.
616pub fn (mut db DB) release_savepoint(savepoint string) ! {
617 if !savepoint.is_identifier() {
618 return error('savepoint should be a identifier string')
619 }
620 db.exec('RELEASE SAVEPOINT ${savepoint};')!
621}
622
623// tables returns the names of all user tables in the database.
624pub fn (db &DB) tables() ![]string {
625 rows :=
626 db.exec("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")!
627 return rows.map(it.vals[0])
628}
629
630// columns returns the column names for the given table.
631pub fn (db &DB) columns(table string) ![]string {
632 escaped := table.replace('"', '""')
633 rows := db.exec('PRAGMA table_info("${escaped}")')!
634 return rows.map(it.vals[1])
635}
636
637// schema returns the CREATE statement(s) for the given table, or for all
638// objects if table is empty.
639pub fn (db &DB) schema(table string) !string {
640 filter := if table != '' {
641 escaped := table.replace("'", "''")
642 "AND name='${escaped}'"
643 } else {
644 ''
645 }
646 rows :=
647 db.exec("SELECT sql FROM sqlite_master WHERE type IN ('table','index','view','trigger') ${filter} AND sql IS NOT NULL ORDER BY type, name")!
648 return rows.map(it.vals[0]).join('\n\n')
649}
650
651// db_size returns the database file size in bytes, computed from page_count
652// and page_size.
653pub fn (db &DB) db_size() !i64 {
654 pc := db.exec('PRAGMA page_count')!
655 ps := db.exec('PRAGMA page_size')!
656 if pc.len == 0 || ps.len == 0 {
657 return 0
658 }
659 return pc[0].vals[0].i64() * ps[0].vals[0].i64()
660}
661
662// reset returns the connection to initial state for reuse
663pub fn (mut db DB) reset() ! {
664}
665
666// validate checks if the connection is still usable
667pub fn (mut db DB) validate() !bool {
668 return db.exec_none('SELECT 1') == 100
669}
670