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