diff --git a/vlib/db/sqlite/sqlite.c.v b/vlib/db/sqlite/sqlite.c.v index f06997edc7ca86..bf0cf76434c209 100644 --- a/vlib/db/sqlite/sqlite.c.v +++ b/vlib/db/sqlite/sqlite.c.v @@ -1,5 +1,7 @@ module sqlite +import regex { regex_opt } + $if freebsd || openbsd { #flag -I/usr/local/include #flag -L/usr/local/lib @@ -85,6 +87,11 @@ pub mut: vals []string } +pub struct QuerySet { +pub mut: + vals map[string]string +} + // fn C.sqlite3_open(&char, &&C.sqlite3) int @@ -248,6 +255,101 @@ pub fn (db &DB) exec(query string) ![]Row { return rows } +// get_queryset returns the values resulting from a 'SELECT' query and the name of each column. If an alias is provided either through the 'as' command or not, the returned value becomes the alias. +@[manualfree] +pub fn (db &DB) get_queryset(query string) ![]QuerySet { + query_lower := query.to_lower() + + // 'select' syntax verified on: https://www.sqlite.org/lang_select.html and + // https://www.sqlite.org/syntax/join-clause.html + mut select_header := regex_opt(r'select((\s)+(all)|(distinct)(\s)+)|(\s)+')! + mut from := regex_opt(r'(\s)+from(\s)+')! + + // or do not include 'FROM', just like 'SELECT 1 + 1' + if query_lower.count('select') == 1 && query_lower.contains('from') { + // The execution of this function indicates that the passed + // query is syntactically correct, which is why no additional verification + rows := db.exec(query)! + + defer { + unsafe { rows.free() } + } + + if rows.len == 0 { + return []QuerySet{} + } else { + // Finding final index of select((\s)+(all)|(distinct)(\s)+)|(\s)+ inside query_lower string + _, end_select := select_header.match_string(query_lower) + + // Finding initial and final index of (\s)+from(\s)+ inside query_lower string + init_from, end_from := from.find(query_lower) + + // Get fields possibly separated by ',' like: select field_1, field_2 as f2, from table_name + fields := query.substr(end_select, init_from).split(',') + + mut query_set := []QuerySet{} + mut tuple := map[string]string{} + + if fields[0] == '*' { + table_name := query.substr(end_from, query.len).replace(';', '').split(' ')[0] + + // Get all fields + all_columns_name := db.exec('pragma table_info(${table_name})')! + + defer { + unsafe { all_columns_name.free() } + } + + mut i := 0 + + for row in rows { + for i < row.vals.len { + // position 1 has a database column attribute + tuple[all_columns_name[i].vals[1]] = row.vals[i] + i++ + } + i = 0 + + query_set << QuerySet{tuple} + tuple = map[string]string{} + } + } else { + mut i := 0 + + for row in rows { + for field in fields { + // verifying formats like: + // select column_1 as alias_column_1 from table_name -> alias creation with 'as' + // select column_1 alias_column_1 from table_name -> alias creationg without 'as' + // select column_1 from table_name -> with alias being column_1 + all_field_alias := if field.contains('as') { + field.split('as') + } else { + field.split(' ') + } + + alias := all_field_alias[all_field_alias.len - 1].replace(' ', + '') + tuple[alias] = row.vals[i] + i++ + } + + i = 0 + query_set << QuerySet{tuple} + tuple = map[string]string{} + } + } + + return query_set + } + } else { + return &SQLError{ + msg: 'This is not a selection query or contains subqueries' + code: sqlite.sqlite_done + } + } +} + // 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] diff --git a/vlib/db/sqlite/sqlite_test.v b/vlib/db/sqlite/sqlite_test.v index 4bd5be062deb56..fde0da65ac66ff 100644 --- a/vlib/db/sqlite/sqlite_test.v +++ b/vlib/db/sqlite/sqlite_test.v @@ -61,7 +61,7 @@ fn test_sqlite() { assert users.len == 4 code := db.exec_none('vacuum') assert code == 101 - user := db.exec_one('select * from users where id = 3') or { panic(err) } + user := db.exec_one('select * from users where id = 3')! println(user) assert user.vals.len == 2 @@ -79,7 +79,73 @@ fn test_sqlite() { db.exec("delete from users where name='Sam'")! assert db.get_affected_rows_count() == 1 - db.close() or { panic(err) } + db.get_queryset('SELECT * FROM users')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('Select * From users')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select * from users')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('SELECT * FROM users')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('Select * From users')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select * from users')! + // assert db.get_affected_rows_count() == 1 + + db.get_queryset('SELECT name, id FROM users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('Select name as n From users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select name n from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('SELECT name, id FROM users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('Select name as name From users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select name n from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('SELECT * FROM users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select id from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select id as i, name as n from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select id _id, name _n from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select max(id), name from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select max(id) as max, name from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select max(id) from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select max(id) max, name n from users WHERE id = 3')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select * from users;')! + assert db.get_affected_rows_count() == 1 + + db.get_queryset('select * from users ;')! + assert db.get_affected_rows_count() == 1 + + db.close()! assert !db.is_open }