mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
db.sqlite: make functions return results, breaking change (#19093)
This commit is contained in:
parent
d0cc564089
commit
76b4c92848
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)!
|
||||
```
|
||||
|
@ -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)
|
||||
|
@ -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;')!
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) }
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user