db.sqlite: make functions return results, breaking change (#19093)

This commit is contained in:
jacksonmowry 2023-08-10 02:39:32 +00:00 committed by GitHub
parent d0cc564089
commit 76b4c92848
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 75 additions and 81 deletions

View File

@ -2,20 +2,19 @@ import db.sqlite
fn main() {
db := sqlite.connect(':memory:')!
db.exec("create table users (id integer primary key, name text default '');")
db.exec("create table users (id integer primary key, name text default '');") or { panic(err) }
db.exec("insert into users (name) values ('Sam')")
db.exec("insert into users (name) values ('Peter')")
db.exec("insert into users (name) values ('Kate')")
db.exec("insert into users (name) values ('Sam')")!
db.exec("insert into users (name) values ('Peter')")!
db.exec("insert into users (name) values ('Kate')")!
nr_users := db.q_int('select count(*) from users')
nr_users := db.q_int('select count(*) from users')!
println('nr users = ${nr_users}')
name := db.q_string('select name from users where id = 1')
name := db.q_string('select name from users where id = 1')!
assert name == 'Sam'
users, code := db.exec('select * from users')
println('SQL Result code: ${code}')
users := db.exec('select * from users')!
for row in users {
println(row.vals)
}

View File

@ -44,8 +44,8 @@ pub fn (mut app App) index() vweb.Result {
fn (mut app App) update_db() !int {
mut db := mydb()!
db.exec('INSERT INTO visits (created_at) VALUES ("")')
visits := db.q_int('SELECT count(*) FROM visits')
db.exec('INSERT INTO visits (created_at) VALUES ("")')!
visits := db.q_int('SELECT count(*) FROM visits')!
db.close()!
return visits
}
@ -56,10 +56,10 @@ fn main() {
println('`v -d vweb_livereload watch --keep run examples/vwatch/web_server/`')
println('')
mut db := mydb()!
db.exec('CREATE TABLE visits (id integer primary key AUTOINCREMENT, created_at timestamp default current_timestamp);')
db.exec('CREATE TABLE visits (id integer primary key AUTOINCREMENT, created_at timestamp default current_timestamp);')!
db.exec('CREATE TRIGGER INSERT_visits AFTER INSERT ON visits BEGIN
UPDATE visits SET created_at = datetime("now", "localtime") WHERE rowid = new.rowid ;
END')
END')!
db.close()!
vweb.run(&App{}, 19123)
}

View File

@ -34,6 +34,6 @@ For instance:
import db.sqlite
db := sqlite.connect('foo.db') or { panic(err) }
db.synchronization_mode(sqlite.SyncMode.off)
db.journal_mode(sqlite.JournalMode.memory)
db.synchronization_mode(sqlite.SyncMode.off)!
db.journal_mode(sqlite.JournalMode.memory)!
```

View File

@ -75,7 +75,7 @@ pub fn (db DB) delete(table string, where orm.QueryData) ! {
pub fn (db DB) last_id() int {
query := 'SELECT last_insert_rowid();'
return db.q_int(query)
return db.q_int(query) or { 0 }
}
// DDL (table creation/destroying etc)

View File

@ -131,7 +131,7 @@ pub fn connect(path string) !DB {
code := C.sqlite3_open(&char(path.str), &db)
if code != 0 {
return &SQLError{
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db))) }
code: code
}
}
@ -150,7 +150,7 @@ pub fn (mut db DB) close() !bool {
db.is_open = false
} else {
return &SQLError{
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
code: code
}
}
@ -180,41 +180,50 @@ pub fn (db &DB) get_affected_rows_count() int {
return C.sqlite3_changes(db.conn)
}
// q_int returns a single integer value, from the first column of the result of executing `query`
pub fn (db &DB) q_int(query string) int {
// q_int returns a single integer value, from the first column of the result of executing `query`, or an error on failure
pub fn (db &DB) q_int(query string) !int {
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
C.sqlite3_step(stmt)
code := C.sqlite3_step(stmt)
if code != sqlite.sqlite_row {
return db.error_message(code, query)
}
res := C.sqlite3_column_int(stmt, 0)
return res
}
// q_string returns a single string value, from the first column of the result of executing `query`
pub fn (db &DB) q_string(query string) string {
// q_string returns a single string value, from the first column of the result of executing `query`, or an error on failure
pub fn (db &DB) q_string(query string) !string {
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
C.sqlite3_step(stmt)
code := C.sqlite3_step(stmt)
if code != sqlite.sqlite_row {
return db.error_message(code, query)
}
val := unsafe { &u8(C.sqlite3_column_text(stmt, 0)) }
return if val != &u8(0) { unsafe { tos_clone(val) } } else { '' }
}
// exec executes the query on the given `db`, and returns an array of all the results, alongside any result code.
// Result codes: https://www.sqlite.org/rescode.html
// exec executes the query on the given `db`, and returns an array of all the results, or an error on failure
[manualfree]
pub fn (db &DB) exec(query string) ([]Row, int) {
pub fn (db &DB) exec(query string) ![]Row {
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
if code != sqlite.sqlite_ok {
return db.error_message(code, query)
}
nr_cols := C.sqlite3_column_count(stmt)
mut res := 0
mut rows := []Row{}
@ -236,26 +245,21 @@ pub fn (db &DB) exec(query string) ([]Row, int) {
}
rows << row
}
return rows, res
return rows
}
// exec_one executes a query on the given `db`.
// It returns either the first row from the result, if the query was successful, or an error.
[manualfree]
pub fn (db &DB) exec_one(query string) !Row {
rows, code := db.exec(query)
rows := db.exec(query)!
defer {
unsafe { rows.free() }
}
if rows.len == 0 {
return &SQLError{
msg: 'No rows'
code: code
}
} else if code != 101 {
return &SQLError{
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
code: code
code: sqlite.sqlite_done
}
}
res := rows[0]
@ -295,21 +299,13 @@ pub fn (db &DB) exec_param_many(query string, params []string) ![]Row {
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), -1, &stmt, 0)
if code != 0 {
return &SQLError{
msg: unsafe {
cstring_to_vstring(&char(C.sqlite3_errstr(code)))
}
code: code
}
return db.error_message(code, query)
}
for i, param in params {
code = C.sqlite3_bind_text(stmt, i + 1, voidptr(param.str), param.len, 0)
if code != 0 {
return &SQLError{
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
code: code
}
return db.error_message(code, query)
}
}
@ -345,8 +341,8 @@ pub fn (db &DB) exec_param(query string, param string) ![]Row {
// create_table issues a "create table if not exists" command to the db.
// It creates the table named 'table_name', with columns generated from 'columns' array.
// The default columns type will be TEXT.
pub fn (db &DB) create_table(table_name string, columns []string) {
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')
pub fn (db &DB) create_table(table_name string, columns []string) ! {
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')!
}
// busy_timeout sets a busy timeout in milliseconds.
@ -359,37 +355,39 @@ pub fn (db &DB) busy_timeout(ms int) int {
// synchronization_mode sets disk synchronization mode, which controls how
// aggressively SQLite will write data to physical storage.
// If the command fails to execute an error is returned
// .off: No syncs at all. (fastest)
// .normal: Sync after each sequence of critical disk operations.
// .full: Sync after each critical disk operation (slowest).
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) {
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) ! {
if sync_mode == .off {
db.exec('pragma synchronous = OFF;')
db.exec('pragma synchronous = OFF;')!
} else if sync_mode == .full {
db.exec('pragma synchronous = FULL;')
db.exec('pragma synchronous = FULL;')!
} else {
db.exec('pragma synchronous = NORMAL;')
db.exec('pragma synchronous = NORMAL;')!
}
}
// journal_mode controls how the journal file is stored and processed.
// If the command fails to execute an error is returned
// .off: No journal record is kept. (fastest)
// .memory: Journal record is held in memory, rather than on disk.
// .delete: At the conclusion of a transaction, journal file is deleted.
// .truncate: Journal file is truncated to a length of zero bytes.
// .persist: Journal file is left in place, but the header is overwritten to indicate journal is no longer valid.
pub fn (db &DB) journal_mode(journal_mode JournalMode) {
pub fn (db &DB) journal_mode(journal_mode JournalMode) ! {
if journal_mode == .off {
db.exec('pragma journal_mode = OFF;')
db.exec('pragma journal_mode = OFF;')!
} else if journal_mode == .delete {
db.exec('pragma journal_mode = DELETE;')
db.exec('pragma journal_mode = DELETE;')!
} else if journal_mode == .truncate {
db.exec('pragma journal_mode = TRUNCATE;')
db.exec('pragma journal_mode = TRUNCATE;')!
} else if journal_mode == .persist {
db.exec('pragma journal_mode = PERSIST;')
db.exec('pragma journal_mode = PERSIST;')!
} else if journal_mode == .memory {
db.exec('pragma journal_mode = MEMORY;')
db.exec('pragma journal_mode = MEMORY;')!
} else {
db.exec('pragma journal_mode = MEMORY;')
db.exec('pragma journal_mode = MEMORY;')!
}
}

View File

@ -103,11 +103,10 @@ fn test_sqlite_orm() {
create table TestCustomSqlType
}!
mut result_custom_sql, mut exec_custom_code := db.exec('
mut result_custom_sql := db.exec('
pragma table_info(TestCustomSqlType);
')
')!
assert exec_custom_code == 101
mut table_info_types_results := []string{}
information_schema_custom_sql := ['INTEGER', 'INTEGER', 'TEXT', 'REAL', 'NUMERIC', 'TEXT',
'INTEGER', 'INTEGER']
@ -128,11 +127,10 @@ fn test_sqlite_orm() {
create table TestDefaultAtribute
}!
mut result_default_sql, mut code := db.exec('
mut result_default_sql := db.exec('
pragma table_info(TestDefaultAtribute);
')
')!
assert code == 101
mut information_schema_data_types_results := []string{}
information_schema_default_sql := ['', '', 'CURRENT_TIME', 'CURRENT_DATE', 'CURRENT_TIMESTAMP']
@ -176,7 +174,7 @@ fn test_get_affected_rows_count() {
db.exec('create table EntityToTest(
id integer not null constraint tbl_pk primary key,
smth integer
);')
);')!
fst := EntityToTest{
id: 1

View File

@ -35,49 +35,48 @@ fn test_sqlite() {
}
mut db := sqlite.connect(':memory:') or { panic(err) }
assert db.is_open
db.exec('drop table if exists users')
db.exec("create table users (id integer primary key, name text default '');")
db.exec("insert into users (name) values ('Sam')")
db.exec('drop table if exists users')!
db.exec("create table users (id integer primary key, name text default '');")!
db.exec("insert into users (name) values ('Sam')")!
assert db.last_insert_rowid() == 1
assert db.get_affected_rows_count() == 1
db.exec("insert into users (name) values ('Peter')")
db.exec("insert into users (name) values ('Peter')")!
assert db.last_insert_rowid() == 2
db.exec("insert into users (name) values ('Kate')")
db.exec("insert into users (name) values ('Kate')")!
assert db.last_insert_rowid() == 3
db.exec_param('insert into users (name) values (?)', 'Tom')!
assert db.last_insert_rowid() == 4
nr_users := db.q_int('select count(*) from users')
nr_users := db.q_int('select count(*) from users')!
assert nr_users == 4
name := db.q_string('select name from users where id = 1')
name := db.q_string('select name from users where id = 1')!
assert name == 'Sam'
username := db.exec_param('select name from users where id = ?', '1')!
assert username[0].vals[0] == 'Sam'
// this insert will be rejected due to duplicated id
db.exec("insert into users (id,name) values (1,'Sam')")
db.exec("insert into users (id,name) values (1,'Sam')")!
assert db.get_affected_rows_count() == 0
users, mut code := db.exec('select * from users')
users := db.exec('select * from users')!
assert users.len == 4
assert code == 101
code = db.exec_none('vacuum')
code := db.exec_none('vacuum')
assert code == 101
user := db.exec_one('select * from users where id = 3') or { panic(err) }
println(user)
assert user.vals.len == 2
db.exec("update users set name='zzzz' where name='qqqq'")
db.exec("update users set name='zzzz' where name='qqqq'")!
assert db.get_affected_rows_count() == 0
db.exec("update users set name='Peter1' where name='Peter'")
db.exec("update users set name='Peter1' where name='Peter'")!
assert db.get_affected_rows_count() == 1
db.exec_param_many('update users set name=? where name=?', ['Peter', 'Peter1'])!
assert db.get_affected_rows_count() == 1
db.exec("delete from users where name='qqqq'")
db.exec("delete from users where name='qqqq'")!
assert db.get_affected_rows_count() == 0
db.exec("delete from users where name='Sam'")
db.exec("delete from users where name='Sam'")!
assert db.get_affected_rows_count() == 1
db.close() or { panic(err) }

View File

@ -23,7 +23,7 @@ fn test_ensure_db_exists_and_user_table_is_ok() {
assert true
eprintln('> drop pre-existing User table...')
db.exec('drop table if exists User')
db.exec('drop table if exists User')!
eprintln('> creating User table...')
sql db {