Skip to content

Commit 76b4c92

Browse files
authored
db.sqlite: make functions return results, breaking change (#19093)
1 parent d0cc564 commit 76b4c92

8 files changed

Lines changed: 75 additions & 81 deletions

File tree

examples/database/sqlite.v

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@ import db.sqlite
22

33
fn main() {
44
db := sqlite.connect(':memory:')!
5-
db.exec("create table users (id integer primary key, name text default '');")
5+
db.exec("create table users (id integer primary key, name text default '');") or { panic(err) }
66

7-
db.exec("insert into users (name) values ('Sam')")
8-
db.exec("insert into users (name) values ('Peter')")
9-
db.exec("insert into users (name) values ('Kate')")
7+
db.exec("insert into users (name) values ('Sam')")!
8+
db.exec("insert into users (name) values ('Peter')")!
9+
db.exec("insert into users (name) values ('Kate')")!
1010

11-
nr_users := db.q_int('select count(*) from users')
11+
nr_users := db.q_int('select count(*) from users')!
1212
println('nr users = ${nr_users}')
1313

14-
name := db.q_string('select name from users where id = 1')
14+
name := db.q_string('select name from users where id = 1')!
1515
assert name == 'Sam'
1616

17-
users, code := db.exec('select * from users')
18-
println('SQL Result code: ${code}')
17+
users := db.exec('select * from users')!
1918
for row in users {
2019
println(row.vals)
2120
}

examples/vwatch/web_server/main.v

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ pub fn (mut app App) index() vweb.Result {
4444

4545
fn (mut app App) update_db() !int {
4646
mut db := mydb()!
47-
db.exec('INSERT INTO visits (created_at) VALUES ("")')
48-
visits := db.q_int('SELECT count(*) FROM visits')
47+
db.exec('INSERT INTO visits (created_at) VALUES ("")')!
48+
visits := db.q_int('SELECT count(*) FROM visits')!
4949
db.close()!
5050
return visits
5151
}
@@ -56,10 +56,10 @@ fn main() {
5656
println('`v -d vweb_livereload watch --keep run examples/vwatch/web_server/`')
5757
println('')
5858
mut db := mydb()!
59-
db.exec('CREATE TABLE visits (id integer primary key AUTOINCREMENT, created_at timestamp default current_timestamp);')
59+
db.exec('CREATE TABLE visits (id integer primary key AUTOINCREMENT, created_at timestamp default current_timestamp);')!
6060
db.exec('CREATE TRIGGER INSERT_visits AFTER INSERT ON visits BEGIN
6161
UPDATE visits SET created_at = datetime("now", "localtime") WHERE rowid = new.rowid ;
62-
END')
62+
END')!
6363
db.close()!
6464
vweb.run(&App{}, 19123)
6565
}

vlib/db/sqlite/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ For instance:
3434
import db.sqlite
3535
3636
db := sqlite.connect('foo.db') or { panic(err) }
37-
db.synchronization_mode(sqlite.SyncMode.off)
38-
db.journal_mode(sqlite.JournalMode.memory)
37+
db.synchronization_mode(sqlite.SyncMode.off)!
38+
db.journal_mode(sqlite.JournalMode.memory)!
3939
```

vlib/db/sqlite/orm.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub fn (db DB) delete(table string, where orm.QueryData) ! {
7575
pub fn (db DB) last_id() int {
7676
query := 'SELECT last_insert_rowid();'
7777

78-
return db.q_int(query)
78+
return db.q_int(query) or { 0 }
7979
}
8080

8181
// DDL (table creation/destroying etc)

vlib/db/sqlite/sqlite.v

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub fn connect(path string) !DB {
131131
code := C.sqlite3_open(&char(path.str), &db)
132132
if code != 0 {
133133
return &SQLError{
134-
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
134+
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db))) }
135135
code: code
136136
}
137137
}
@@ -150,7 +150,7 @@ pub fn (mut db DB) close() !bool {
150150
db.is_open = false
151151
} else {
152152
return &SQLError{
153-
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
153+
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
154154
code: code
155155
}
156156
}
@@ -180,41 +180,50 @@ pub fn (db &DB) get_affected_rows_count() int {
180180
return C.sqlite3_changes(db.conn)
181181
}
182182

183-
// q_int returns a single integer value, from the first column of the result of executing `query`
184-
pub fn (db &DB) q_int(query string) int {
183+
// q_int returns a single integer value, from the first column of the result of executing `query`, or an error on failure
184+
pub fn (db &DB) q_int(query string) !int {
185185
stmt := &C.sqlite3_stmt(unsafe { nil })
186186
defer {
187187
C.sqlite3_finalize(stmt)
188188
}
189189
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
190-
C.sqlite3_step(stmt)
190+
code := C.sqlite3_step(stmt)
191+
if code != sqlite.sqlite_row {
192+
return db.error_message(code, query)
193+
}
191194

192195
res := C.sqlite3_column_int(stmt, 0)
193196
return res
194197
}
195198

196-
// q_string returns a single string value, from the first column of the result of executing `query`
197-
pub fn (db &DB) q_string(query string) string {
199+
// q_string returns a single string value, from the first column of the result of executing `query`, or an error on failure
200+
pub fn (db &DB) q_string(query string) !string {
198201
stmt := &C.sqlite3_stmt(unsafe { nil })
199202
defer {
200203
C.sqlite3_finalize(stmt)
201204
}
202205
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
203-
C.sqlite3_step(stmt)
206+
code := C.sqlite3_step(stmt)
207+
if code != sqlite.sqlite_row {
208+
return db.error_message(code, query)
209+
}
204210

205211
val := unsafe { &u8(C.sqlite3_column_text(stmt, 0)) }
206212
return if val != &u8(0) { unsafe { tos_clone(val) } } else { '' }
207213
}
208214

209-
// exec executes the query on the given `db`, and returns an array of all the results, alongside any result code.
210-
// Result codes: https://www.sqlite.org/rescode.html
215+
// exec executes the query on the given `db`, and returns an array of all the results, or an error on failure
211216
[manualfree]
212-
pub fn (db &DB) exec(query string) ([]Row, int) {
217+
pub fn (db &DB) exec(query string) ![]Row {
213218
stmt := &C.sqlite3_stmt(unsafe { nil })
214219
defer {
215220
C.sqlite3_finalize(stmt)
216221
}
217-
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
222+
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
223+
if code != sqlite.sqlite_ok {
224+
return db.error_message(code, query)
225+
}
226+
218227
nr_cols := C.sqlite3_column_count(stmt)
219228
mut res := 0
220229
mut rows := []Row{}
@@ -236,26 +245,21 @@ pub fn (db &DB) exec(query string) ([]Row, int) {
236245
}
237246
rows << row
238247
}
239-
return rows, res
248+
return rows
240249
}
241250

242251
// exec_one executes a query on the given `db`.
243252
// It returns either the first row from the result, if the query was successful, or an error.
244253
[manualfree]
245254
pub fn (db &DB) exec_one(query string) !Row {
246-
rows, code := db.exec(query)
255+
rows := db.exec(query)!
247256
defer {
248257
unsafe { rows.free() }
249258
}
250259
if rows.len == 0 {
251260
return &SQLError{
252261
msg: 'No rows'
253-
code: code
254-
}
255-
} else if code != 101 {
256-
return &SQLError{
257-
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
258-
code: code
262+
code: sqlite.sqlite_done
259263
}
260264
}
261265
res := rows[0]
@@ -295,21 +299,13 @@ pub fn (db &DB) exec_param_many(query string, params []string) ![]Row {
295299

296300
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), -1, &stmt, 0)
297301
if code != 0 {
298-
return &SQLError{
299-
msg: unsafe {
300-
cstring_to_vstring(&char(C.sqlite3_errstr(code)))
301-
}
302-
code: code
303-
}
302+
return db.error_message(code, query)
304303
}
305304

306305
for i, param in params {
307306
code = C.sqlite3_bind_text(stmt, i + 1, voidptr(param.str), param.len, 0)
308307
if code != 0 {
309-
return &SQLError{
310-
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
311-
code: code
312-
}
308+
return db.error_message(code, query)
313309
}
314310
}
315311

@@ -345,8 +341,8 @@ pub fn (db &DB) exec_param(query string, param string) ![]Row {
345341
// create_table issues a "create table if not exists" command to the db.
346342
// It creates the table named 'table_name', with columns generated from 'columns' array.
347343
// The default columns type will be TEXT.
348-
pub fn (db &DB) create_table(table_name string, columns []string) {
349-
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')
344+
pub fn (db &DB) create_table(table_name string, columns []string) ! {
345+
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')!
350346
}
351347

352348
// busy_timeout sets a busy timeout in milliseconds.
@@ -359,37 +355,39 @@ pub fn (db &DB) busy_timeout(ms int) int {
359355

360356
// synchronization_mode sets disk synchronization mode, which controls how
361357
// aggressively SQLite will write data to physical storage.
358+
// If the command fails to execute an error is returned
362359
// .off: No syncs at all. (fastest)
363360
// .normal: Sync after each sequence of critical disk operations.
364361
// .full: Sync after each critical disk operation (slowest).
365-
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) {
362+
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) ! {
366363
if sync_mode == .off {
367-
db.exec('pragma synchronous = OFF;')
364+
db.exec('pragma synchronous = OFF;')!
368365
} else if sync_mode == .full {
369-
db.exec('pragma synchronous = FULL;')
366+
db.exec('pragma synchronous = FULL;')!
370367
} else {
371-
db.exec('pragma synchronous = NORMAL;')
368+
db.exec('pragma synchronous = NORMAL;')!
372369
}
373370
}
374371

375372
// journal_mode controls how the journal file is stored and processed.
373+
// If the command fails to execute an error is returned
376374
// .off: No journal record is kept. (fastest)
377375
// .memory: Journal record is held in memory, rather than on disk.
378376
// .delete: At the conclusion of a transaction, journal file is deleted.
379377
// .truncate: Journal file is truncated to a length of zero bytes.
380378
// .persist: Journal file is left in place, but the header is overwritten to indicate journal is no longer valid.
381-
pub fn (db &DB) journal_mode(journal_mode JournalMode) {
379+
pub fn (db &DB) journal_mode(journal_mode JournalMode) ! {
382380
if journal_mode == .off {
383-
db.exec('pragma journal_mode = OFF;')
381+
db.exec('pragma journal_mode = OFF;')!
384382
} else if journal_mode == .delete {
385-
db.exec('pragma journal_mode = DELETE;')
383+
db.exec('pragma journal_mode = DELETE;')!
386384
} else if journal_mode == .truncate {
387-
db.exec('pragma journal_mode = TRUNCATE;')
385+
db.exec('pragma journal_mode = TRUNCATE;')!
388386
} else if journal_mode == .persist {
389-
db.exec('pragma journal_mode = PERSIST;')
387+
db.exec('pragma journal_mode = PERSIST;')!
390388
} else if journal_mode == .memory {
391-
db.exec('pragma journal_mode = MEMORY;')
389+
db.exec('pragma journal_mode = MEMORY;')!
392390
} else {
393-
db.exec('pragma journal_mode = MEMORY;')
391+
db.exec('pragma journal_mode = MEMORY;')!
394392
}
395393
}

vlib/db/sqlite/sqlite_orm_test.v

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,10 @@ fn test_sqlite_orm() {
103103
create table TestCustomSqlType
104104
}!
105105

106-
mut result_custom_sql, mut exec_custom_code := db.exec('
106+
mut result_custom_sql := db.exec('
107107
pragma table_info(TestCustomSqlType);
108-
')
108+
')!
109109

110-
assert exec_custom_code == 101
111110
mut table_info_types_results := []string{}
112111
information_schema_custom_sql := ['INTEGER', 'INTEGER', 'TEXT', 'REAL', 'NUMERIC', 'TEXT',
113112
'INTEGER', 'INTEGER']
@@ -128,11 +127,10 @@ fn test_sqlite_orm() {
128127
create table TestDefaultAtribute
129128
}!
130129

131-
mut result_default_sql, mut code := db.exec('
130+
mut result_default_sql := db.exec('
132131
pragma table_info(TestDefaultAtribute);
133-
')
132+
')!
134133

135-
assert code == 101
136134
mut information_schema_data_types_results := []string{}
137135
information_schema_default_sql := ['', '', 'CURRENT_TIME', 'CURRENT_DATE', 'CURRENT_TIMESTAMP']
138136

@@ -176,7 +174,7 @@ fn test_get_affected_rows_count() {
176174
db.exec('create table EntityToTest(
177175
id integer not null constraint tbl_pk primary key,
178176
smth integer
179-
);')
177+
);')!
180178

181179
fst := EntityToTest{
182180
id: 1

vlib/db/sqlite/sqlite_test.v

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,49 +35,48 @@ fn test_sqlite() {
3535
}
3636
mut db := sqlite.connect(':memory:') or { panic(err) }
3737
assert db.is_open
38-
db.exec('drop table if exists users')
39-
db.exec("create table users (id integer primary key, name text default '');")
40-
db.exec("insert into users (name) values ('Sam')")
38+
db.exec('drop table if exists users')!
39+
db.exec("create table users (id integer primary key, name text default '');")!
40+
db.exec("insert into users (name) values ('Sam')")!
4141
assert db.last_insert_rowid() == 1
4242
assert db.get_affected_rows_count() == 1
43-
db.exec("insert into users (name) values ('Peter')")
43+
db.exec("insert into users (name) values ('Peter')")!
4444
assert db.last_insert_rowid() == 2
45-
db.exec("insert into users (name) values ('Kate')")
45+
db.exec("insert into users (name) values ('Kate')")!
4646
assert db.last_insert_rowid() == 3
4747
db.exec_param('insert into users (name) values (?)', 'Tom')!
4848
assert db.last_insert_rowid() == 4
49-
nr_users := db.q_int('select count(*) from users')
49+
nr_users := db.q_int('select count(*) from users')!
5050
assert nr_users == 4
51-
name := db.q_string('select name from users where id = 1')
51+
name := db.q_string('select name from users where id = 1')!
5252
assert name == 'Sam'
5353
username := db.exec_param('select name from users where id = ?', '1')!
5454
assert username[0].vals[0] == 'Sam'
5555

5656
// this insert will be rejected due to duplicated id
57-
db.exec("insert into users (id,name) values (1,'Sam')")
57+
db.exec("insert into users (id,name) values (1,'Sam')")!
5858
assert db.get_affected_rows_count() == 0
5959

60-
users, mut code := db.exec('select * from users')
60+
users := db.exec('select * from users')!
6161
assert users.len == 4
62-
assert code == 101
63-
code = db.exec_none('vacuum')
62+
code := db.exec_none('vacuum')
6463
assert code == 101
6564
user := db.exec_one('select * from users where id = 3') or { panic(err) }
6665
println(user)
6766
assert user.vals.len == 2
6867

69-
db.exec("update users set name='zzzz' where name='qqqq'")
68+
db.exec("update users set name='zzzz' where name='qqqq'")!
7069
assert db.get_affected_rows_count() == 0
7170

72-
db.exec("update users set name='Peter1' where name='Peter'")
71+
db.exec("update users set name='Peter1' where name='Peter'")!
7372
assert db.get_affected_rows_count() == 1
7473
db.exec_param_many('update users set name=? where name=?', ['Peter', 'Peter1'])!
7574
assert db.get_affected_rows_count() == 1
7675

77-
db.exec("delete from users where name='qqqq'")
76+
db.exec("delete from users where name='qqqq'")!
7877
assert db.get_affected_rows_count() == 0
7978

80-
db.exec("delete from users where name='Sam'")
79+
db.exec("delete from users where name='Sam'")!
8180
assert db.get_affected_rows_count() == 1
8281

8382
db.close() or { panic(err) }

vlib/orm/orm_sql_or_blocks_test.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn test_ensure_db_exists_and_user_table_is_ok() {
2323
assert true
2424

2525
eprintln('> drop pre-existing User table...')
26-
db.exec('drop table if exists User')
26+
db.exec('drop table if exists User')!
2727

2828
eprintln('> creating User table...')
2929
sql db {

0 commit comments

Comments
 (0)