{"id":13805062,"url":"https://github.com/lydiandy/vsql","last_synced_at":"2026-03-13T19:47:26.136Z","repository":{"id":48907101,"uuid":"271346966","full_name":"lydiandy/vsql","owner":"lydiandy","description":"A sql query builder for V","archived":false,"fork":false,"pushed_at":"2021-10-21T16:52:48.000Z","size":332,"stargazers_count":46,"open_issues_count":1,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-06T14:07:44.136Z","etag":null,"topics":["knex","sql","vlang","vsql"],"latest_commit_sha":null,"homepage":"","language":"V","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lydiandy.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-10T17:53:16.000Z","updated_at":"2025-01-20T12:30:06.000Z","dependencies_parsed_at":"2022-09-14T18:20:32.999Z","dependency_job_id":null,"html_url":"https://github.com/lydiandy/vsql","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydiandy%2Fvsql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydiandy%2Fvsql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydiandy%2Fvsql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lydiandy%2Fvsql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lydiandy","download_url":"https://codeload.github.com/lydiandy/vsql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254003490,"owners_count":21997896,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["knex","sql","vlang","vsql"],"created_at":"2024-08-04T01:00:57.096Z","updated_at":"2026-03-13T19:47:26.080Z","avatar_url":"https://github.com/lydiandy.png","language":"V","readme":"## vsql\n\n**vsql is just wip,do not use in production,but it can works now**\n\n- just a sql query builder,not orm\n\n- easy to learn,easy to use\n\n- support multi-dialect:pg,mysql,sqlite,mssql, by now just pg as the first version\n\n- method call chain \n\n### main idea\n\nthe main idea of vsql is:  **method call chain =\u003e ast =\u003e sql**\n\n### some limit\n\nhere are some limits,maybe need to find better solution,advice is welcome~\n\n- select is a key word of vlang,so have to use select_\n- in query statement,at the end of every method call chain need end() to know the end of chain and start generate sql. Is it possible to remove it?\n- by now,there is no database interface for driver like go,not easy to support multi-dialect\n\n### example\n\ncreate table first:\n\n```v\nmodule main\n\nimport vsql\n\nfn main() {\n\tconfig := vsql.Config{\n\t\tclient: 'pg'\n\t\thost: 'localhost'\n\t\tport: 5432\n\t\tuser: 'postgres' // change to your user\n\t\tpassword: '' // chagne to your password\n\t\tdatabase: 'test_db' // change to your database\n\t}\n\t// connect to database with config\n\tmut db := vsql.connect(config) or { panic('connect error:$err') }\n\t// create table person\n\tdb.exec('drop table if exists person')\n\tdb.exec(\"create table person (id integer primary key, name text default '',age integer default 0,income integer default 0);\")\n\t// insert data\n\tdb.exec(\"insert into person (id,name,age,income) values (1,'tom',29,1000)\")\n\tdb.exec(\"insert into person (id,name,age,income) values (2,'jack',33,500)\")\n\tdb.exec(\"insert into person (id,name,age,income) values (3,'mary',25,2000)\")\n\tdb.exec(\"insert into person (id,name,age,income) values (4,'lisa',25,1000)\")\n\tdb.exec(\"insert into person (id,name,age,income) values (5,'andy',18,0)\")\n\t// create table cat\n\tdb.exec('drop table if exists cat')\n\tdb.exec(\"create table cat (id integer primary key,name text default '',owner_id integer)\")\n\t// insert data\n\tdb.exec(\"insert into cat (id,name,owner_id) values (1,'cat1',1)\")\n\tdb.exec(\"insert into cat (id,name,owner_id) values (2,'cat2',3)\")\n\tdb.exec(\"insert into cat (id,name,owner_id) values (3,'cat3',5)\")\n\t// create table food\n\tdb.exec('drop table if exists food')\n\tdb.exec(\"create table food (id integer primary key,name text default '',cat_id integer)\")\n\t// insert data\n\tdb.exec(\"insert into food (id,name,cat_id) values (1,'food1',1)\")\n\tdb.exec(\"insert into food (id,name,cat_id) values (2,'food2',3)\")\n\tdb.exec(\"insert into food (id,name,cat_id) values (3,'food3',0)\")\n\t// for test create table,drop person2\n\tdb.exec('drop table if exists person2')\n\tdb.exec('drop table if exists new_person')\n}\n```\n\n```v\nmodule main\n\nimport vsql\n\nfn main() {\n\t// config to connect db,by now just support pg\n\tconfig := vsql.Config{\n\t\tclient: 'pg'\n\t\thost: 'localhost'\n\t\tport: 5432\n\t\tuser: 'postgres'\n\t\tpassword: ''\n\t\tdatabase: 'test_db'\n\t}\n\t// connect to database with config\n\tmut db := vsql.connect(config) or { panic('connect error:$err') }\n\t// start to use db\n\tres := db.table('person').column('*').end()\n\tprintln(res)\n}\n```\n\nall the sql statement can be found in example or test directory\n\n### select\n\n#### table+column\n\n```sql\nres := db.table('person').column('*').end()\nselect * from person\n\nres := db.table('person').column('id,name,age').end()\nselect id,name,age from person\n```\n\n#### select+from\n\nselect is key word of v,so use select_\n\n```sql\nres :=db.select_('*').from('person').end()\nselect * from person\n\nres :=db.select_('id,name,age,income').from('person').end()\nselect * from person\n```\n\n#### where\n\n```sql\n//where\nres := db.table('person').column('id,name,age').where('id=1').end()\nselect id,name,age from person where (id=1)\n\n// or where\nres:= db.table('person').column('id,name,age').where('id=1').or_where('id=2').end()\nselect id,name,age from person where (id=1) or (id=2)\n\n// and where\nres:= db.table('person').column('id,name,age').where('id=1').and_where('age=29').end()\nselect id,name,age from person where (id=1) and (age=29)\n\n// where not\nres:= db.table('person').column('id,name,age').where('id=1').where_not('age=0').end()\nselect id,name,age from person where (id=1) and not (age=0)\n\n// or where not\nres:= db.table('person').column('id,name,age').where('id=1').or_where_not('age=0').end()\nselect id,name,age from person where (id=1) or not (age=0)\n```\n\nwhere in\n\n```sql\n//where in\nres := db.table('person').column('id,name,age').where_in('id', ['1', '2', '3']).end()\nselect id,name,age from person where (id in (1,2,3))\n\n// or where in\nres := db.table('person').column('id,name,age').where('id=1').or_where_in('id', ['1', '2', '3']).end()\nselect id,name,age from person where (id=1) or (id in (1,2,3))\n\n// and where in\nres := db.table('person').column('id,name,age').where('id=1').and_where_in('id', ['1', '2', '3']).end()\nselect id,name,age from person where (id=1) and (id in (1,2,3))\n\n// where not in\nres := db.table('person').column('id,name,age').where('id=1').where_not_in('id', ['2', '3']).end()\nselect id,name,age from person where (id=1) and not (id in (2,3))\n\n// or where not in\nres := db.table('person').column('id,name,age').where('id=1').or_where_not_in('id', ['2', '3']).end()\nselect id,name,age from person where (id=1) or not (id in (2,3))\n```\n\nwhere null\n\n```sql\n//where null\nres := db.table('person').column('id,name,age').where('id\u003e1').where_null('income).end()\nselect id,name,age from person where (id\u003e1) and (income is null)\n                                                                         \n//or where null\nres := db.table('person').column('id,name,age').where('id\u003e1').or_where_null('income').end()\nselect id,name,age from person where (id\u003e1) or (income is null)\n                                                                         \n//and where null\nres := db.table('person').column('id,name,age').where('id\u003e1').and_where_null('income').end()\nselect id,name,age from person where (id\u003e1) and (income is null)\n                                                                         \n//where not null\nres := db.table('person').column('id,name,age').where('id\u003e1').where_not_null('income').end()\nselect id,name,age from person where (id\u003e1) and not (income is null)\n                                                                         \n//or where not null\nres := db.table('person').column('id,name,age').where('id\u003e1').or_where_not_null('income').end()\nselect id,name,age from person where (id\u003e1) or not (income is null)\n```\n\nwhere between\n\n```sql\n//where between\nres := db.table('person').column('id,name,age,income').where('id\u003e1').where_between('income',['100','1000']).end()\nselect id,name,age,income from person where (id\u003e1) and (income between 100 and 1000)\n\n//or where between\nres := db.table('person').column('id,name,age,income').where('id\u003e1').or_where_between('income',['100','1000']).end()\nselect id,name,age,income from person where (id\u003e1) or (income between 100 and 1000)\n\n//and where between\nres := db.table('person').column('id,name,age,income').where('id\u003e1').and_where_between('income',['100','1000']).end()\nselect id,name,age,income from person where (id\u003e1) and (income between 100 and 1000)\n\n//where not between\nres := db.table('person').column('id,name,age,income').where('id\u003e1').where_not_between('income',['100','1000']).end()\nselect id,name,age,income from person where (id\u003e1) and not (income between 100 and 1000)\n\n//or where not between\nres := db.table('person').column('id,name,age,income').where('id\u003e1').or_where_not_between('income',['100','1000']).end()\nselect id,name,age,income from person where (id\u003e1) or not (income between 100 and 1000)\n```\n\nwhere exists\n\n```sql\n//where exists\nres := db.table('person').column('id,name,age,income').where('id\u003e1').where_exists('select income from person where income\u003e1000').end()\nselect id,name,age,income from person where (id\u003e1) and exists (select income from person where income\u003e1000)\n\n//or where exists\nres := db.table('person').column('id,name,age,income').where('id\u003e1').or_where_exists('select income from person where income\u003e1000').end()\nselect id,name,age,income from person where (id\u003e1) or exists (select income from person where income\u003e1000)\n\n//and where exists\nres := db.table('person').column('id,name,age,income').where('id\u003e1').and_where_exists('select income from person where income\u003e1000').end()\nselect id,name,age,income from person where (id\u003e1) and exists (select income from person where income\u003e1000)\n\n//where not exists\nres := db.table('person').column('id,name,age,income').where('id\u003e1').where_not_exists('select income from person where income\u003e1000').end()\nselect id,name,age,income from person where (id\u003e1) and not exists (select income from person where income\u003e1000)\n\n//or where not exists\nres := db.table('person').column('id,name,age,income').where('id\u003e1').or_where_not_exists('select income from person where income\u003e1000').end()\nselect id,name,age,income from person where (id\u003e1) or not exists (select income from person where income\u003e1000)\n```\n\nwhere raw\n\n```sql\nres := db.table('person').where_raw('id=?', '1').end()\nselect * from person where id=1\n```\n\n#### first/offset/limit\n\n```sql\n// first\nres := db.table('person').column('').first().end()\nselect * from person limit 1\n// limit\nres := db.table('person').column('').limit(3).end()\nselect * from person limit 3\n// offset\nres := db.table('person').column('').offset(1).end()\nselect * from person offset 1\n// offset+limit\nres := db.table('person').column('').offset(2).limit(2).end()\nelect * from person offset 2 limit 2\n```\n\n#### distinct\n\n```sql\nres := db.table('person').column('id,name,age').distinct().end()\nselect distinct id,name,age from person\n```\n\n#### order by\n\n```sql\nres := db.table('person').column('*').order_by('name desc').order_by('age').end()\nselect * from person order by name desc,age asc\n\nres := db.table('person').column('').order_by_raw('name desc,age asc').end()\nselect * from person order by name desc,age asc\n```\n\n#### group by/having\n\n```sql\nres := db.table('person').column('age,count(age)').group_by('age').group_by('name').end()\nselect age,count(age) from person group by age,name\nres := db.table('person').column('age,count(age)').group_by_raw('age,income').end()\nselect age,count(age) from person group by age,income\n//having\nres = db.table('person').column('age,count(age),avg(income)').group_by('age').having('count(*)=2').end()\nselect age,count(age),avg(income) from person group by age having count(*)=2\n```\n\n#### join\n\n```sql\n//inner join\nres := db.table('cat as c').column('c.id,c.name,p.name,p.age').inner_join('person as p','c.owner_id=p.id').end()\n\t\t\nselect c.id,c.name,p.name,p.age from cat as c inner join person as p on c.owner_id=p.id'\n\n// left join\nres := db.table('cat as c').column('c.id,c.name,p.name,p.age').left_join('person as p','c.owner_id=p.id').end()\nselect c.id,c.name,p.name,p.age from cat as c left join person as p on c.owner_id=p.id\n\n// right join\nres := db.table('cat as c').column('c.id,c.name,p.name,p.age').right_join('person as p','c.owner_id=p.id').end()\nselect c.id,c.name,p.name,p.age from cat as c right join person as p on c.owner_id=p.id\n\n// outer join\nres := db.table('cat as c').column('c.id,c.name,p.name,p.age').outer_join('person as p','c.owner_id=p.id').end()\nselect c.id,c.name,p.name,p.age from cat as c full outer join person as p on c.owner_id=p.id\n\n// cross join\nres := db.table('cat as c').column('c.id,c.name,p.name,p.age').cross_join('person as p').end()\nselect c.id,c.name,p.name,p.age from cat as c cross join person as p\n\n// join raw\nres := db.table('cat as c').column('c.id,c.name,p.name,p.age').join_raw('join person as p on c.owner_id=p.id').end()\nselect c.id,c.name,p.name,p.age from cat as c join person as p on c.owner_id=p.id\n\n// multi join\nres := db.table('cat as c').column('c.id,c.name,p.name,p.age,f.name').left_join('person as p','c.owner_id=p.id').left_join('food as f', 'c.id=f.cat_id').end()\nselect c.id,c.name,p.name,p.age,f.name from cat as c left join person as p on c.owner_id=p.id left join food as f on c.id=f.cat_id\n```\n\n#### aggregate function\n\n```sql\nres := db.table('person').count('*').end()\nselect count(*) from person\n\nres := db.table('person').count('* as rows').end()\nselect count(*) as rows from person\n\nres := db.table('person').count('distinct name as n').end()\nselect distinct count(name) as n from person\n\nres := db.table('person').min('age').end()\nselect min(age) from person\n\nres := db.table('person').max('age').end()\nselect max(age) from person\n\nres := db.table('person').min('age as min_age').max('age as max_age').end()\nselect min(age) as min_age,max(age) as max_age from person\n\nres := db.table('person').sum('income').end()\nselect sum(income) from person\n\nres := db.table('person').avg('income').end()\nselect avg(income) from person\n```\n\n#### union\n\nunion is a key word of v,so use union_()\n\n```sql\nstmt1 := db.table('person').column('id,name').where('id=1').to_sql()\nstmt2 := db.table('person').column('id,name').where('id=2').to_sql()\nstmt3 := db.table('person').column('id,name').where('id=3').to_sql()\nres = db.table('person').column('id,name').where('id=4').union_(stmt1, stmt2, stmt3).end()\nres = db.table('person').column('id,name').where('id=4').union_all(stmt1, stmt2, stmt3).end()\nres = db.table('person').column('id,name').where('id=4').intersect(stmt1, stmt2, stmt3).end()\nres = db.table('person').column('id,name').where('id=4').except(stmt1, stmt2, stmt3).end()\n```\n\n### insert\n\n```sql\nres := db.table('person').insert({\n\t\t'id': '255'\n\t\t'name': 'abc'\n\t\t'age': '36'\n\t}).end()\ninsert into person (id,name,age) values ('255','abc','36')\n\nres := db.table('person').insert({\n\t\t'id': '255'\n\t\t'name': 'abc'\n\t\t'age': '36'\n\t}).returning('id', 'name').end()\ninsert into person (id,name,age) values ('255','abc','36') returning id,name\n\nres := db.insert({\n\t\t'id': '12'\n\t\t'name': 'tom'\n\t}).into('person').returning('id').end()\ninsert into person (id,name) values ('12','tom') returning id\n```\n\n### update\n\n```sql\nres := db.table('person').update({\n\t\t'name': 'paris'\n\t}).where('id=1').returning('id').end()\nupdate person set name='paris' where (id=1) returning id\n\nres := db.table('person').update({\n\t\t'name': 'paris'\n\t\t'age': '32'\n\t}).where('id=1').returning('id').end()\nupdate person set name='paris',age='32' where (id=1) returning id\n```\n\n### delete\n\n```sql\nres := db.table('person').delete().where('id=3').end()\ndelete from person where (id=3)\n\nres := db.table('person').where('id=2').delete().end()\ndelete from person where (id=2)\n```\n\n### schema\n\n#### create table\n\n```v\n// create table\nmut table := db.create_table('person2') \n\n//create column\n//table.increment('id').primary()\ntable.increment('id')          \ntable.boolean('is_ok')\n//string is a key word of v, so use string_\ntable.string_('open_id', 255).size(100).unique()\ntable.datetime('attend_time')\ntable.string_('form_id', 255).not_null() \ntable.integer('is_send').default_to('1')\ntable.decimal('amount', 10, 2).not_null().check('amount\u003e0')\n//table constraint\ntable.primary(['id'])\ntable.unique(['id'])\ntable.check('amount\u003e30')\ntable.check('amount\u003c60')\n\n//exec create table sql\ntable.exec()\n```\n\n#### alter table\n\n```sql\nres := db.rename_table('person', 'new_person')\n```\n\n#### drop table\n\n```sql\nres := db.drop_table('food')\n```\n\n### transaction\n\n```c\nt := db.transaction()\n// t := db.tx() //the shorter fn\nt.exec(\"insert into person (id,name,age,income) values (33,'name33',33,0)\")\nt.exec(\"insert into person (id,name,age,income) values (44,'name44',44,0)\")\nt.exec(\"insert into person (id,name,age,income) values (55,'name55',55,0)\")\n// t.rollback()\nt.commit()\n```\n\n### raw sql\n\n```sql\ndb.exec('drop table if exists person')\ndb.exec(\"create table person (id integer primary key, name text default '',age integer default 0,income integer default 0);\")\n\ndb.exec(\"insert into person (id,name,age,income) values (1,'tom',29,1000)\")\ndb.exec(\"insert into person (id,name,age,income) values (2,'jack',33,500)\")\ndb.exec(\"insert into person (id,name,age,income) values (3,'mary',25,2000)\")\ndb.exec(\"insert into person (id,name,age,income) values (4,'lisa',25,1000)\")\ndb.exec(\"insert into person (id,name,age,income) values (5,'andy',18,0)\")\n\ndb.exec('drop table if exists cat')\ndb.exec(\"create table cat (id integer primary key,name text default '',owner_id integer)\")\n```\n\n### other\n\n**print_sql**\n\nIf you need to print the sql string,you can use print_sql() before end().\n\n```sql\ndb.table('person').column('id,name,age').where('id=1').print_sql().end()\n```\n\n```sql\nselect id,name,age from person where (id=1)\n```\n\n**print_obj**\n\nIf you need to print the object struct of sql,you can you print_obj() before end().\n\n```sql\ndb.table('person').column('id,name,age').where('id=1').print_obj().end()\n```\n\n```\nvsql.Select {\n    table_name: 'person'\n    table_alias: ''\n    is_distinct: false\n    columns: [vsql.Column {\n    name: 'id'\n    alias: ''\n}, vsql.Column {\n    name: 'name'\n    alias: ''\n}, vsql.Column {\n    name: 'age'\n    alias: ''\n}]\n    where: [vsql.Where {\n    typ: 'where'\n    operator: ''\n    condition: 'id=1'\n    column_name: ''\n    range: []\n    exist_stmt: ''\n}]\n    join: []\n    join_raw: ''\n    first: false\n    limit: 0\n    offset: 0\n    order_by: []\n    order_by_raw: ''\n    group_by: []\n    group_by_raw: ''\n    having: ''\n    aggregate_fn: []\n    timeout: 0\n}\n```\n\n**to_sql**\n\nto_sql() is used to test. It will not execute the sql,just generate the sql string.\n\n```c\nres := db.table('person').column('*').to_sql()\nassert res == 'select * from person'\nres := db.table('person').column('id,name,age').to_sql()\nassert res == 'select id,name,age from person'\n```\n\n###  todo\n\n```c\nmodule main\n\n//sql interface\nimport database.sql\n\n//use std driver\nimport database.pg\nimport database.mysql\nimport database.sqlite\nimport database.mssql\n//or use driver in dialect\nimport vsql.dialect.pg\nimport vsql.dialect.mysql\nimport vsql.dialect.sqlite\nimport vsql.dialect.mssql\n\n\t//select\nres:=db.table('person').column('id,name,age').where('id',3).to(\u0026person).end()\n\n\t//shcema ddl\n\tdb.alter_table('person',fn(t Table) {\n\t\tt.drop_column('form_id')\n\t\tt.drop_columns('form_id','is_send')\n\t\tt.drop_index('form_id')\n\t\tt.drop_foreign('form_id')\n\t\tt.drop_unique('form_id')\n\t\tt.drop_primary('form_id')\n\t\tt.drop_column('name')\n\t\tt.rename_column('old','new')\n\t\tt.str('new_column')\n\t})\n\n\t//transaction\n  \n\t//way two\n\tdb.transaction(fn(t Transaction) {\n\t\tdb.table('person')...\n\t\tdb.table('person')...\n\t\tt.rollback()\n\t})\n}\n\n//model\n// [db:person]\npub struct person {\n\tid          int \t\t\t[json:id,db:id]\n\tuuid        string\t\t\t[json:uuid,db:'name:=uuid;size=100;uinique;primary_key']\n\topen_id     string\t\t\t[json:open_id,db:open_id]\n\tattend_time time.datetime\t[json:atten_time,db:attend_time]\n\tform_id     string\t\t\t[json:form_id,db:form_id]\n\tis_send     int   \t\t\t[json:is_send,db:is_send]\n}\npub fn (u person)table_name() string {\n\treturn 'person'\n}\n\n//model migration\ndb.up()\ndb.down()\n\n```\n\n### run vsql test\n\nall the test sql statement are in vsql/test, you can run the test by:\n\n```shell\nv test vsql/test\n```\n\n\n\n## Acknowledgments\n\nInspired by [knex](https://github.com/knex/knex) that  was my favorite sql query builder before I meet V.\n\n## License\n\nLicensed under [MIT](license)","funding_links":[],"categories":["Libraries"],"sub_categories":["Database clients"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flydiandy%2Fvsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flydiandy%2Fvsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flydiandy%2Fvsql/lists"}