diff --git a/vlib/mysql/mysql_orm_test.v b/vlib/mysql/mysql_orm_test.v index bed6e23fed..db74aff0cd 100644 --- a/vlib/mysql/mysql_orm_test.v +++ b/vlib/mysql/mysql_orm_test.v @@ -1,6 +1,7 @@ import orm import mysql import time +import v.ast struct TestCustomSqlType { id int [primary; sql: serial] @@ -29,20 +30,28 @@ mut: deleted_at time.Time } +struct TestDefaultAtribute { + id string [primary; sql: serial] + name string + created_at string [default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP'] +} + fn test_mysql_orm() { - mut mdb := mysql.Connection{ + mut db := mysql.Connection{ host: 'localhost' port: 3306 username: 'root' password: '' dbname: 'mysql' } - mdb.connect() or { panic(err) } - db := orm.Connection(mdb) + db.connect() or { panic(err) } + defer { + db.close() + } db.create('Test', [ orm.TableField{ name: 'id' - typ: 7 + typ: ast.int_type_idx attrs: [ StructAttribute{ name: 'primary' @@ -57,12 +66,12 @@ fn test_mysql_orm() { }, orm.TableField{ name: 'name' - typ: 20 + typ: ast.string_type_idx attrs: [] }, orm.TableField{ name: 'age' - typ: 7 + typ: ast.int_type_idx }, ]) or { panic(err) } @@ -75,11 +84,11 @@ fn test_mysql_orm() { table: 'Test' has_where: true fields: ['id', 'name', 'age'] - types: [7, 20, 8] + types: [ast.int_type_idx, ast.string_type_idx, ast.i64_type_idx] }, orm.QueryData{}, orm.QueryData{ fields: ['name', 'age'] data: [orm.Primitive('Louis'), i64(101)] - types: [20, 8] + types: [ast.string_type_idx, ast.i64_type_idx] is_and: [true, true] kinds: [.eq, .eq] }) or { panic(err) } @@ -88,8 +97,6 @@ fn test_mysql_orm() { name := res[0][1] age := res[0][2] - mdb.close() - assert id is int if id is int { assert id == 1 @@ -104,22 +111,10 @@ fn test_mysql_orm() { if age is i64 { assert age == 101 } -} - -fn test_orm() { - mut db := mysql.Connection{ - host: 'localhost' - port: 3306 - username: 'root' - password: '' - dbname: 'mysql' - } - - db.connect() or { - println(err) - panic(err) - } + /** test orm sql type + * - verify if all type create by attribute sql_type has created + */ sql db { create table TestCustomSqlType } @@ -168,27 +163,19 @@ fn test_orm() { sql db { drop table TestCustomSqlType } - db.close() assert result_custom_sql.maps() == information_schema_custom_sql -} -fn test_orm_time_type() ? { - mut db := mysql.Connection{ - host: 'localhost' - port: 3306 - username: 'root' - password: '' - dbname: 'mysql' - } - - db.connect() or { + /** test_orm_time_type + * - test time.Time v type with sql_type: 'TIMESTAMP' + * - test string v type with sql_type: 'TIMESTAMP' + * - test time.Time v type without + */ + today := time.parse('2022-07-16 15:13:27') or { println(err) panic(err) } - today := time.parse('2022-07-16 15:13:27')? - model := TestTimeType{ username: 'hitalo' created_at: today @@ -212,10 +199,38 @@ fn test_orm_time_type() ? { drop table TestTimeType } - db.close() - assert results[0].username == model.username assert results[0].created_at == model.created_at assert results[0].updated_at == model.updated_at assert results[0].deleted_at == model.deleted_at + + /** test default attribute + */ + sql db { + create table TestDefaultAtribute + } + + mut result_defaults := db.query(" + SELECT COLUMN_DEFAULT + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'TestDefaultAtribute' + ORDER BY ORDINAL_POSITION + ") or { + println(err) + panic(err) + } + mut information_schema_defaults_results := []string{} + + sql db { + drop table TestDefaultAtribute + } + + information_schema_column_default_sql := [{ + 'COLUMN_DEFAULT': '' + }, { + 'COLUMN_DEFAULT': '' + }, { + 'COLUMN_DEFAULT': 'CURRENT_TIMESTAMP' + }] + assert information_schema_column_default_sql == result_defaults.maps() } diff --git a/vlib/mysql/orm.v b/vlib/mysql/orm.v index 578fcb8baa..c10bda762b 100644 --- a/vlib/mysql/orm.v +++ b/vlib/mysql/orm.v @@ -124,7 +124,7 @@ pub fn (db Connection) insert(table string, data orm.QueryData) ? { mut converted_primitive_array := db.factory_orm_primitive_converted_from_sql(table, data)? - converted_data := orm.QueryData{ + converted_primitive_data := orm.QueryData{ fields: data.fields data: converted_primitive_array types: [] @@ -132,17 +132,19 @@ pub fn (db Connection) insert(table string, data orm.QueryData) ? { is_and: [] } - query := orm.orm_stmt_gen(table, '`', .insert, false, '?', 1, converted_data, orm.QueryData{}) + query, converted_data := orm.orm_stmt_gen(table, '`', .insert, false, '?', 1, converted_primitive_data, + orm.QueryData{}) mysql_stmt_worker(db, query, converted_data, orm.QueryData{})? } pub fn (db Connection) update(table string, data orm.QueryData, where orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '`', .update, false, '?', 1, data, where) + query, _ := orm.orm_stmt_gen(table, '`', .update, false, '?', 1, data, where) mysql_stmt_worker(db, query, data, where)? } pub fn (db Connection) delete(table string, where orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '`', .delete, false, '?', 1, orm.QueryData{}, where) + query, _ := orm.orm_stmt_gen(table, '`', .delete, false, '?', 1, orm.QueryData{}, + where) mysql_stmt_worker(db, query, orm.QueryData{}, where)? } @@ -158,7 +160,7 @@ pub fn (db Connection) last_id() orm.Primitive { // table pub fn (db Connection) create(table string, fields []orm.TableField) ? { - query := orm.orm_table_gen(table, '`', false, 0, fields, mysql_type_from_v, false) or { + query := orm.orm_table_gen(table, '`', true, 0, fields, mysql_type_from_v, false) or { return err } mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})? diff --git a/vlib/orm/README.md b/vlib/orm/README.md index d41377ec56..32478ef529 100644 --- a/vlib/orm/README.md +++ b/vlib/orm/README.md @@ -15,6 +15,7 @@ - `[sql: type]` where `type` is a V type such as `int` or `f64`, or special type `serial` - `[sql: 'name']` sets a custom column name for the field - `[sql_type: 'SQL TYPE']` sets the sql type which is used in sql +- `[default: 'sql defaults']` sets the default value or function when create a new table ## Usage @@ -90,3 +91,53 @@ result := sql db { select from Foo where id > 1 order by id } ``` + +### Example +```v ignore +import pg + +struct Member { + id string [default: 'gen_random_uuid()'; primary; sql_type: 'uuid'] + name string + created_at string [default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP'] +} + +fn main() { + db := pg.connect(pg.Config{ + host: 'localhost' + port: 5432 + user: 'user' + password: 'password' + dbname: 'dbname' + }) or { + println(err) + return + } + + defer { + db.close() + } + + sql db { + create table Member + } + + new_member := Member{ + name: 'John Doe' + } + + sql db { + insert new_member into Member + } + + selected_member := sql db { + select from Member where name == 'John Doe' limit 1 + } + + sql db { + update Member set name = 'Hitalo' where id == selected_member.id + } +} + + +``` \ No newline at end of file diff --git a/vlib/orm/orm.v b/vlib/orm/orm.v index e24a2526db..206397bac3 100644 --- a/vlib/orm/orm.v +++ b/vlib/orm/orm.v @@ -182,27 +182,42 @@ pub interface Connection { // num - Stmt uses nums at prepared statements (? or ?1) // qm - Character for prepared statment, qm because of quotation mark like in sqlite // start_pos - When num is true, it's the start position of the counter -pub fn orm_stmt_gen(table string, q string, kind StmtKind, num bool, qm string, start_pos int, data QueryData, where QueryData) string { +pub fn orm_stmt_gen(table string, q string, kind StmtKind, num bool, qm string, start_pos int, data QueryData, where QueryData) (string, QueryData) { mut str := '' - mut c := start_pos + mut data_fields := []string{} + mut data_data := []Primitive{} match kind { .insert { mut values := []string{} + mut select_fields := []string{} - for _ in 0 .. data.fields.len { - // loop over the length of data.field and generate ?0, ?1 or just ? based on the $num qmeter for value placeholders - if num { - values << '$qm$c' - c++ - } else { - values << '$qm' + for i in 0 .. data.fields.len { + if data.data.len > 0 { + match data.data[i].type_name() { + 'string' { + if (data.data[i] as string).len == 0 { + continue + } + } + 'time.Time' { + if (data.data[i] as time.Time).unix == 0 { + continue + } + } + else {} + } + data_data << data.data[i] } + select_fields << '$q${data.fields[i]}$q' + values << factory_insert_qm_value(num, qm, c) + data_fields << data.fields[i] + c++ } str += 'INSERT INTO $q$table$q (' - str += data.fields.map('$q$it$q').join(', ') + str += select_fields.join(', ') str += ') VALUES (' str += values.join(', ') str += ')' @@ -262,7 +277,13 @@ pub fn orm_stmt_gen(table string, q string, kind StmtKind, num bool, qm string, } } str += ';' - return str + return str, QueryData{ + fields: data_fields + data: data_data + types: data.types + kinds: data.kinds + is_and: data.is_and + } } // Generates an sql select stmt, from universal parameter @@ -357,6 +378,7 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, if field.is_arr { continue } + mut default_val := field.default_val mut no_null := false mut is_unique := false mut is_skip := false @@ -397,6 +419,14 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, } ctyp = attr.arg } + 'default' { + if attr.kind != .string { + return error("default attribute need be string. Try [default: '$attr.arg'] instead of [default: $attr.arg]") + } + if default_val == '' { + default_val = attr.arg + } + } /*'fkey' { if attr.arg != '' { if attr.kind == .string { @@ -416,8 +446,8 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, return error('Unknown type ($field.typ) for field $field.name in struct $table') } stmt = '$q$field_name$q $ctyp' - if defaults && field.default_val != '' { - stmt += ' DEFAULT $field.default_val' + if defaults && default_val != '' { + stmt += ' DEFAULT $default_val' } if no_null { stmt += ' NOT NULL' @@ -543,3 +573,11 @@ pub fn time_to_primitive(b time.Time) Primitive { pub fn infix_to_primitive(b InfixType) Primitive { return Primitive(b) } + +fn factory_insert_qm_value(num bool, qm string, c int) string { + if num { + return '$qm$c' + } else { + return '$qm' + } +} diff --git a/vlib/orm/orm_fn_test.v b/vlib/orm/orm_fn_test.v index f8eedbf320..f354ceb09e 100644 --- a/vlib/orm/orm_fn_test.v +++ b/vlib/orm/orm_fn_test.v @@ -2,7 +2,7 @@ import orm import v.ast fn test_orm_stmt_gen_update() { - query := orm.orm_stmt_gen('Test', "'", .update, true, '?', 0, orm.QueryData{ + query, _ := orm.orm_stmt_gen('Test', "'", .update, true, '?', 0, orm.QueryData{ fields: ['test', 'a'] data: [] types: [] @@ -17,7 +17,7 @@ fn test_orm_stmt_gen_update() { } fn test_orm_stmt_gen_insert() { - query := orm.orm_stmt_gen('Test', "'", .insert, true, '?', 0, orm.QueryData{ + query, _ := orm.orm_stmt_gen('Test', "'", .insert, true, '?', 0, orm.QueryData{ fields: ['test', 'a'] data: [] types: [] @@ -27,7 +27,7 @@ fn test_orm_stmt_gen_insert() { } fn test_orm_stmt_gen_delete() { - query := orm.orm_stmt_gen('Test', "'", .delete, true, '?', 0, orm.QueryData{ + query, _ := orm.orm_stmt_gen('Test', "'", .delete, true, '?', 0, orm.QueryData{ fields: ['test', 'a'] data: [] types: [] diff --git a/vlib/pg/orm.v b/vlib/pg/orm.v index a145667701..2fc41a54d1 100644 --- a/vlib/pg/orm.v +++ b/vlib/pg/orm.v @@ -31,17 +31,18 @@ pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.Qu // sql stmt pub fn (db DB) insert(table string, data orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '"', .insert, true, '$', 1, data, orm.QueryData{}) - pg_stmt_worker(db, query, data, orm.QueryData{})? + query, converted_data := orm.orm_stmt_gen(table, '"', .insert, true, '$', 1, data, + orm.QueryData{}) + pg_stmt_worker(db, query, converted_data, orm.QueryData{})? } pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '"', .update, true, '$', 1, data, where) + query, _ := orm.orm_stmt_gen(table, '"', .update, true, '$', 1, data, where) pg_stmt_worker(db, query, data, where)? } pub fn (db DB) delete(table string, where orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '"', .delete, true, '$', 1, orm.QueryData{}, where) + query, _ := orm.orm_stmt_gen(table, '"', .delete, true, '$', 1, orm.QueryData{}, where) pg_stmt_worker(db, query, orm.QueryData{}, where)? } @@ -163,18 +164,20 @@ fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats formats << 1 } string { - types << u32(Oid.t_text) + // If paramTypes is NULL, or any particular element in the array is zero, + // the server infers a data type for the parameter symbol in the same way + // it would do for an untyped literal string. + types << &u8(0) vals << data.str lens << data.len formats << 0 } time.Time { - types << u32(Oid.t_int4) - unix := int(data.unix) - num := conv.htn32(unsafe { &u32(&unix) }) - vals << &char(&num) - lens << int(sizeof(u32)) - formats << 1 + datetime := ((d as time.Time).format_ss() as string) + types << &u8(0) + vals << datetime.str + lens << datetime.len + formats << 0 } orm.InfixType { pg_stmt_match(mut types, mut vals, mut lens, mut formats, data.right) @@ -190,9 +193,12 @@ fn pg_type_from_v(typ int) ?string { orm.type_idx['bool'] { 'BOOLEAN' } - orm.type_idx['int'], orm.type_idx['u32'], orm.time { + orm.type_idx['int'], orm.type_idx['u32'] { 'INT' } + orm.time { + 'TIMESTAMP' + } orm.type_idx['i64'], orm.type_idx['u64'] { 'BIGINT' } diff --git a/vlib/pg/pg_orm_test.v b/vlib/pg/pg_orm_test.v index eb0fd1266b..2c605f925c 100644 --- a/vlib/pg/pg_orm_test.v +++ b/vlib/pg/pg_orm_test.v @@ -2,39 +2,89 @@ module main import orm import pg +import v.ast +import time + +struct TestCustomSqlType { + id int [primary; sql: serial] + custom string [sql_type: 'TEXT'] + custom1 string [sql_type: 'VARCHAR(191)'] + custom2 string [sql_type: 'TIMESTAMP'] + custom3 string [sql_type: 'uuid'] +} + +struct TestCustomWrongSqlType { + id int [primary; sql: serial] + custom string + custom1 string [sql_type: 'VARCHAR'] + custom2 string [sql_type: 'money'] + custom3 string [sql_type: 'xml'] +} + +struct TestTimeType { +mut: + id int [primary; sql: serial] + username string + created_at time.Time [sql_type: 'TIMESTAMP'] + updated_at string [sql_type: 'TIMESTAMP'] + deleted_at time.Time +} + +struct TestDefaultAtribute { + id string [default: 'gen_random_uuid()'; primary; sql_type: 'uuid'] + name string + created_at string [default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP'] +} fn test_pg_orm() { mut db := pg.connect( host: 'localhost' user: 'postgres' - password: '' + password: 'password' dbname: 'postgres' ) or { panic(err) } + defer { + db.close() + } + db.create('Test', [ orm.TableField{ name: 'id' - typ: 7 + typ: ast.string_type_idx + is_time: false + default_val: '' + is_arr: false attrs: [ StructAttribute{ name: 'primary' + has_arg: false + arg: '' + kind: .plain }, StructAttribute{ name: 'sql' has_arg: true - kind: .plain arg: 'serial' + kind: .plain }, ] }, orm.TableField{ name: 'name' - typ: 18 + typ: ast.string_type_idx + is_time: false + default_val: '' + is_arr: false attrs: [] }, orm.TableField{ name: 'age' - typ: 7 + typ: ast.i64_type_idx + is_time: false + default_val: '' + is_arr: false + attrs: [] }, ]) or { panic(err) } @@ -45,15 +95,22 @@ fn test_pg_orm() { res := db.@select(orm.SelectConfig{ table: 'Test' + is_count: false has_where: true + has_order: false + order: '' + order_type: .asc + has_limit: false + primary: 'id' + has_offset: false fields: ['id', 'name', 'age'] - types: [7, 18, 8] + types: [ast.int_type_idx, ast.string_type_idx, ast.i64_type_idx] }, orm.QueryData{}, orm.QueryData{ - fields: ['name'] - data: [orm.Primitive('Louis'), i64(101)] - types: [18] + fields: ['name', 'age'] + data: [orm.Primitive('Louis'), orm.Primitive(101)] + types: [] + kinds: [.eq, .eq] is_and: [true] - kinds: [.eq] }) or { panic(err) } id := res[0][0] @@ -74,4 +131,97 @@ fn test_pg_orm() { if age is i64 { assert age == 101 } + + /** test orm sql type + * - verify if all type create by attribute sql_type has created + */ + + sql db { + create table TestCustomSqlType + } + + mut result_custom_sql := db.exec(" + SELECT DATA_TYPE + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'TestCustomSqlType' + ORDER BY ORDINAL_POSITION + ") or { + println(err) + panic(err) + } + mut information_schema_data_types_results := []string{} + information_schema_custom_sql := ['integer', 'text', 'character varying', + 'timestamp without time zone', 'uuid'] + for data_type in result_custom_sql { + information_schema_data_types_results << data_type.vals[0] + } + + sql db { + drop table TestCustomSqlType + } + + assert information_schema_data_types_results == information_schema_custom_sql + + /** test_orm_time_type + * - test time.Time v type with sql_type: 'TIMESTAMP' + * - test string v type with sql_type: 'TIMESTAMP' + * - test time.Time v type without + */ + today := time.parse('2022-07-16 15:13:27') or { + println(err) + panic(err) + } + + model := TestTimeType{ + username: 'hitalo' + created_at: today + updated_at: today.str() + deleted_at: today + } + + sql db { + create table TestTimeType + } + + sql db { + insert model into TestTimeType + } + + results := sql db { + select from TestTimeType where username == 'hitalo' + } + + sql db { + drop table TestTimeType + } + + assert results[0].username == model.username + assert results[0].created_at == model.created_at + assert results[0].updated_at == model.updated_at + assert results[0].deleted_at == model.deleted_at + + /** test default attribute + */ + sql db { + create table TestDefaultAtribute + } + + mut result_defaults := db.exec(" + SELECT column_default + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'TestDefaultAtribute' + ORDER BY ORDINAL_POSITION + ") or { + println(err) + panic(err) + } + mut information_schema_defaults_results := []string{} + + for defaults in result_defaults { + information_schema_defaults_results << defaults.vals[0] + } + sql db { + drop table TestDefaultAtribute + } + assert ['gen_random_uuid()', '', 'CURRENT_TIMESTAMP'] == information_schema_defaults_results } diff --git a/vlib/sqlite/orm.v b/vlib/sqlite/orm.v index e01b2d369f..599783a2dc 100644 --- a/vlib/sqlite/orm.v +++ b/vlib/sqlite/orm.v @@ -51,17 +51,18 @@ pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.Qu // sql stmt pub fn (db DB) insert(table string, data orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '`', .insert, true, '?', 1, data, orm.QueryData{}) - sqlite_stmt_worker(db, query, data, orm.QueryData{})? + query, converted_data := orm.orm_stmt_gen(table, '`', .insert, true, '?', 1, data, + orm.QueryData{}) + sqlite_stmt_worker(db, query, converted_data, orm.QueryData{})? } pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '`', .update, true, '?', 1, data, where) + query, _ := orm.orm_stmt_gen(table, '`', .update, true, '?', 1, data, where) sqlite_stmt_worker(db, query, data, where)? } pub fn (db DB) delete(table string, where orm.QueryData) ? { - query := orm.orm_stmt_gen(table, '`', .delete, true, '?', 1, orm.QueryData{}, where) + query, _ := orm.orm_stmt_gen(table, '`', .delete, true, '?', 1, orm.QueryData{}, where) sqlite_stmt_worker(db, query, orm.QueryData{}, where)? } diff --git a/vlib/sqlite/sqlite_orm_test.v b/vlib/sqlite/sqlite_orm_test.v index 4aa714293e..363ec72d9a 100644 --- a/vlib/sqlite/sqlite_orm_test.v +++ b/vlib/sqlite/sqlite_orm_test.v @@ -1,10 +1,32 @@ import orm import sqlite import v.ast +import time + +struct TestCustomSqlType { + id int [primary; sql: serial] + custom string [sql_type: 'INTEGER'] + custom1 string [sql_type: 'TEXT'] + custom2 string [sql_type: 'REAL'] + custom3 string [sql_type: 'NUMERIC'] + custom4 string + custom5 int + custom6 time.Time +} + +struct TestDefaultAtribute { + id string [primary; sql: serial] + name string + created_at string [default: 'CURRENT_TIME'] + created_at1 string [default: 'CURRENT_DATE'] + created_at2 string [default: 'CURRENT_TIMESTAMP'] +} fn test_sqlite_orm() { - sdb := sqlite.connect(':memory:') or { panic(err) } - db := orm.Connection(sdb) + mut db := sqlite.connect(':memory:') or { panic(err) } + defer { + db.close() or { panic(err) } + } db.create('Test', [ orm.TableField{ name: 'id' @@ -68,4 +90,74 @@ fn test_sqlite_orm() { if age is i64 { assert age == 100 } + + /** test orm sql type + * - verify if all type create by attribute sql_type has created + */ + + sql db { + create table TestCustomSqlType + } + + mut result_custom_sql, mut exec_custom_code := 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'] + + for data_type in result_custom_sql { + table_info_types_results << data_type.vals[2] + } + assert table_info_types_results == information_schema_custom_sql + + sql db { + drop table TestCustomSqlType + } + + /** test default attribute + */ + + sql db { + create table TestDefaultAtribute + } + + mut result_default_sql, mut code := 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'] + + for data_type in result_default_sql { + information_schema_data_types_results << data_type.vals[4] + } + assert information_schema_data_types_results == information_schema_default_sql + + test_default_atribute := TestDefaultAtribute{ + name: 'Hitalo' + } + + sql db { + insert test_default_atribute into TestDefaultAtribute + } + + result_test_default_atribute := sql db { + select from TestDefaultAtribute limit 1 + } + + assert result_test_default_atribute.name == 'Hitalo' + assert test_default_atribute.created_at.len == 0 + assert test_default_atribute.created_at1.len == 0 + assert test_default_atribute.created_at2.len == 0 + assert result_test_default_atribute.created_at.len == 8 // HH:MM:SS + assert result_test_default_atribute.created_at1.len == 10 // YYYY-MM-DD + assert result_test_default_atribute.created_at2.len == 19 // YYYY-MM-DD HH:MM:SS + + sql db { + drop table TestDefaultAtribute + } } diff --git a/vlib/sqlite/sqlite_test.v b/vlib/sqlite/sqlite_test.v index 1e72a0c930..22c5bc388d 100644 --- a/vlib/sqlite/sqlite_test.v +++ b/vlib/sqlite/sqlite_test.v @@ -33,6 +33,7 @@ fn test_sqlite() { fn test_can_access_sqlite_result_consts() { assert sqlite.sqlite_ok == 0 assert sqlite.sqlite_error == 1 + // assert sqlite.misuse == 21 assert sqlite.sqlite_row == 100 assert sqlite.sqlite_done == 101 } diff --git a/vlib/sqlite/stmt.v b/vlib/sqlite/stmt.v index da07e821b3..cea2605e57 100644 --- a/vlib/sqlite/stmt.v +++ b/vlib/sqlite/stmt.v @@ -51,6 +51,10 @@ fn (stmt Stmt) get_f64(idx int) f64 { fn (stmt Stmt) get_text(idx int) string { b := &char(C.sqlite3_column_text(stmt.stmt, idx)) + + if b == &char(0) { + return '' + } return unsafe { b.vstring() } }