orm: add mysql support (#9630)
* add mysql to orm * fix got to big packet error * format sql.v * format example * custom sql types * add mysql table cration * add documentation * format sql.v * fix markdown * start implementing select_expr for mysql * remove orm.c * format sql.v * finish mysql expr * remove c * remove unessecary files * change to c implementation * remove c * added str interpolation for idents * fix string insert * fix compilation problems * fix gitly compilation * fix typing mistake * add link to orm docspull/9670/head
parent
9f093203a4
commit
64391efa4d
|
@ -118,9 +118,11 @@ pub fn new_test_session(_vargs string) TestSession {
|
||||||
}
|
}
|
||||||
$if macos {
|
$if macos {
|
||||||
skip_files << 'examples/database/mysql.v'
|
skip_files << 'examples/database/mysql.v'
|
||||||
|
skip_files << 'examples/database/orm.v'
|
||||||
}
|
}
|
||||||
$if windows {
|
$if windows {
|
||||||
skip_files << 'examples/database/mysql.v'
|
skip_files << 'examples/database/mysql.v'
|
||||||
|
skip_files << 'examples/database/orm.v'
|
||||||
skip_files << 'examples/websocket/ping.v' // requires OpenSSL
|
skip_files << 'examples/websocket/ping.v' // requires OpenSSL
|
||||||
skip_files << 'examples/websocket/client-server/client.v' // requires OpenSSL
|
skip_files << 'examples/websocket/client-server/client.v' // requires OpenSSL
|
||||||
skip_files << 'examples/websocket/client-server/server.v' // requires OpenSSL
|
skip_files << 'examples/websocket/client-server/server.v' // requires OpenSSL
|
||||||
|
|
20
doc/docs.md
20
doc/docs.md
|
@ -3147,8 +3147,8 @@ fn test() []int {
|
||||||
|
|
||||||
(This is still in an alpha state)
|
(This is still in an alpha state)
|
||||||
|
|
||||||
V has a built-in ORM (object-relational mapping) which supports SQLite,
|
V has a built-in ORM (object-relational mapping) which supports SQLite and MySQL,
|
||||||
and will soon support MySQL, Postgres, MS SQL, and Oracle.
|
but soon it will support Postgres, MS SQL, and Oracle.
|
||||||
|
|
||||||
V's ORM provides a number of benefits:
|
V's ORM provides a number of benefits:
|
||||||
|
|
||||||
|
@ -3164,20 +3164,26 @@ import sqlite
|
||||||
|
|
||||||
struct Customer {
|
struct Customer {
|
||||||
// struct name has to be the same as the table name (for now)
|
// struct name has to be the same as the table name (for now)
|
||||||
id int // a field named `id` of integer type must be the first field
|
id int [primary; sql: serial] // a field named `id` of integer type must be the first field
|
||||||
name string
|
name string [nonull]
|
||||||
nr_orders int
|
nr_orders int
|
||||||
country string
|
country string [nonull]
|
||||||
}
|
}
|
||||||
|
|
||||||
db := sqlite.connect('customers.db') ?
|
db := sqlite.connect('customers.db') ?
|
||||||
|
|
||||||
|
// you can create tables
|
||||||
|
// CREATE TABLE IF NOT EXISTS `Customer` (`id` INTEGER PRIMARY KEY, `name` TEXT NOT NULL, `nr_orders` INTEGER, `country` TEXT NOT NULL)
|
||||||
|
sql db {
|
||||||
|
create table Customer
|
||||||
|
}
|
||||||
|
|
||||||
// select count(*) from Customer
|
// select count(*) from Customer
|
||||||
nr_customers := sql db {
|
nr_customers := sql db {
|
||||||
select count from Customer
|
select count from Customer
|
||||||
}
|
}
|
||||||
println('number of all customers: $nr_customers')
|
println('number of all customers: $nr_customers')
|
||||||
// V syntax can be used to build queries
|
// V syntax can be used to build queries
|
||||||
// db.select returns an array
|
|
||||||
uk_customers := sql db {
|
uk_customers := sql db {
|
||||||
select from Customer where country == 'uk' && nr_orders > 0
|
select from Customer where country == 'uk' && nr_orders > 0
|
||||||
}
|
}
|
||||||
|
@ -3200,7 +3206,7 @@ sql db {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For more examples, see <a href='https://github.com/vlang/v/blob/master/vlib/orm/orm_test.v'>vlib/orm/orm_test.v</a>.
|
For more examples and the docs, see <a href='https://github.com/vlang/v/tree/master/vlib/orm'>vlib/orm</a>.
|
||||||
|
|
||||||
## Writing Documentation
|
## Writing Documentation
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import sqlite
|
import sqlite
|
||||||
|
import mysql
|
||||||
|
|
||||||
struct Module {
|
struct Module {
|
||||||
id int
|
id int [primary; sql: serial]
|
||||||
name string
|
name string
|
||||||
nr_downloads int
|
nr_downloads int [sql: u64]
|
||||||
creator User
|
creator User
|
||||||
}
|
}
|
||||||
|
|
||||||
struct User {
|
struct User {
|
||||||
id int
|
id int [primary; sql: serial]
|
||||||
age int
|
age int
|
||||||
name string
|
name string [nonull]
|
||||||
is_customer bool
|
is_customer bool
|
||||||
skipped_string string [skip]
|
skipped_string string [skip]
|
||||||
}
|
}
|
||||||
|
@ -18,8 +19,9 @@ struct User {
|
||||||
fn main() {
|
fn main() {
|
||||||
db := sqlite.connect(':memory:') or { panic(err) }
|
db := sqlite.connect(':memory:') or { panic(err) }
|
||||||
db.exec('drop table if exists User')
|
db.exec('drop table if exists User')
|
||||||
db.exec("create table Module (id integer primary key, name text default '', nr_downloads int default 0, creator int default 0);")
|
sql db {
|
||||||
db.exec("create table User (id integer primary key, age int default 0, name text default '', is_customer int default 0);")
|
create table Module
|
||||||
|
}
|
||||||
|
|
||||||
mod := Module{
|
mod := Module{
|
||||||
name: 'test'
|
name: 'test'
|
||||||
|
@ -38,7 +40,41 @@ fn main() {
|
||||||
select from Module where id == 1
|
select from Module where id == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
println(modul.name)
|
eprintln(modul)
|
||||||
println(modul.creator.name)
|
|
||||||
|
|
||||||
|
mysql()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mysql() {
|
||||||
|
mut conn := mysql.Connection{
|
||||||
|
host: 'localhost'
|
||||||
|
port: 3306
|
||||||
|
username: 'root'
|
||||||
|
password: 'abc'
|
||||||
|
dbname: 'test'
|
||||||
|
}
|
||||||
|
conn.connect() or { panic(err) }
|
||||||
|
|
||||||
|
sql conn {
|
||||||
|
create table Module
|
||||||
|
}
|
||||||
|
|
||||||
|
mod := Module{
|
||||||
|
name: 'test'
|
||||||
|
nr_downloads: 10
|
||||||
|
creator: User{
|
||||||
|
age: 21
|
||||||
|
name: 'VUser'
|
||||||
|
is_customer: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sql conn {
|
||||||
|
insert mod into Module
|
||||||
|
}
|
||||||
|
|
||||||
|
m := sql conn {
|
||||||
|
select from Module where id == 1
|
||||||
|
}
|
||||||
|
eprintln(m)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
# ORM
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
|
||||||
|
- `[primary]` set the field as the primary key
|
||||||
|
- `[nonull]` field will be `NOT NULL` in table creation
|
||||||
|
- `[skip]` field will be skipped
|
||||||
|
- `[sql: type]` sets the type which is used in sql (special type `serial`)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```v ignore
|
||||||
|
struct Foo {
|
||||||
|
id int [primary; sql: serial]
|
||||||
|
name string [nonull]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create
|
||||||
|
|
||||||
|
```v ignore
|
||||||
|
sql db {
|
||||||
|
create table Foo
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Insert
|
||||||
|
|
||||||
|
```v ignore
|
||||||
|
var := Foo{
|
||||||
|
name: 'abc'
|
||||||
|
}
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
insert var into Foo
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update
|
||||||
|
|
||||||
|
```v ignore
|
||||||
|
sql db {
|
||||||
|
update Foo set name = 'cde' where name == 'abc'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete
|
||||||
|
```v ignore
|
||||||
|
sql db {
|
||||||
|
delete from Foo where id > 10
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Select
|
||||||
|
```v ignore
|
||||||
|
result := sql db {
|
||||||
|
select from Foo where id == 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```v ignore
|
||||||
|
result := sql db {
|
||||||
|
select from Foo where id > 1 limit 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```v ignore
|
||||||
|
result := sql db {
|
||||||
|
select from Foo where id > 1 order by id
|
||||||
|
}
|
||||||
|
```
|
|
@ -116,6 +116,11 @@ mut:
|
||||||
cur_generic_types []ast.Type // `int`, `string`, etc in `foo<T>()`
|
cur_generic_types []ast.Type // `int`, `string`, etc in `foo<T>()`
|
||||||
sql_i int
|
sql_i int
|
||||||
sql_stmt_name string
|
sql_stmt_name string
|
||||||
|
sql_bind_name string
|
||||||
|
sql_idents []string
|
||||||
|
sql_idents_types []ast.Type
|
||||||
|
sql_left_type ast.Type
|
||||||
|
sql_table_name string
|
||||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||||
inside_vweb_tmpl bool
|
inside_vweb_tmpl bool
|
||||||
inside_return bool
|
inside_return bool
|
||||||
|
|
|
@ -28,11 +28,15 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
|
||||||
g.sql_create_table(node)
|
g.sql_create_table(node)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name
|
||||||
typ := g.parse_db_type(node.db_expr)
|
typ := g.parse_db_type(node.db_expr)
|
||||||
match typ {
|
match typ {
|
||||||
.sqlite3 {
|
.sqlite3 {
|
||||||
g.sqlite3_stmt(node, typ)
|
g.sqlite3_stmt(node, typ)
|
||||||
}
|
}
|
||||||
|
.mysql {
|
||||||
|
g.mysql_stmt(node, typ)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
|
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
|
||||||
}
|
}
|
||||||
|
@ -45,6 +49,9 @@ fn (mut g Gen) sql_create_table(node ast.SqlStmt) {
|
||||||
.sqlite3 {
|
.sqlite3 {
|
||||||
g.sqlite3_create_table(node, typ)
|
g.sqlite3_create_table(node, typ)
|
||||||
}
|
}
|
||||||
|
.mysql {
|
||||||
|
g.mysql_create_table(node, typ)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
|
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
|
||||||
}
|
}
|
||||||
|
@ -52,43 +59,40 @@ fn (mut g Gen) sql_create_table(node ast.SqlStmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) {
|
fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) {
|
||||||
|
g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name
|
||||||
typ := g.parse_db_type(node.db_expr)
|
typ := g.parse_db_type(node.db_expr)
|
||||||
match typ {
|
match typ {
|
||||||
.sqlite3 {
|
.sqlite3 {
|
||||||
g.sqlite3_select_expr(node, sub, line, typ)
|
g.sqlite3_select_expr(node, sub, line, typ)
|
||||||
}
|
}
|
||||||
|
.mysql {
|
||||||
|
g.mysql_select_expr(node, sub, line, typ)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
|
verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) sql_bind_int(val string, typ SqlType) {
|
fn (mut g Gen) sql_bind(val string, len string, real_type ast.Type, typ SqlType) {
|
||||||
match typ {
|
match typ {
|
||||||
.sqlite3 {
|
.sqlite3 {
|
||||||
g.sqlite3_bind_int(val)
|
g.sqlite3_bind(val, len, real_type)
|
||||||
}
|
}
|
||||||
else {
|
.mysql {
|
||||||
// add error
|
g.mysql_bind(val, real_type)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) sql_bind_string(val string, len string, typ SqlType) {
|
|
||||||
match typ {
|
|
||||||
.sqlite3 {
|
|
||||||
g.sqlite3_bind_string(val, len)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// add error
|
|
||||||
}
|
}
|
||||||
|
else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) sql_type_from_v(typ SqlType, v_typ ast.Type) string {
|
fn (mut g Gen) sql_type_from_v(typ SqlType, v_typ ast.Type) string {
|
||||||
match typ {
|
match typ {
|
||||||
.sqlite3 {
|
.sqlite3 {
|
||||||
return g.sqlite3_type_from_v(typ, v_typ)
|
return g.sqlite3_type_from_v(v_typ)
|
||||||
|
}
|
||||||
|
.mysql {
|
||||||
|
return g.mysql_get_table_type(v_typ)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// add error
|
// add error
|
||||||
|
@ -108,51 +112,8 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmt, typ SqlType) {
|
||||||
g.expr(node.db_expr)
|
g.expr(node.db_expr)
|
||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, _SLIT("')
|
g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, _SLIT("')
|
||||||
table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name)
|
g.sql_defaults(node, typ)
|
||||||
if node.kind == .insert {
|
g.writeln(');')
|
||||||
g.write('INSERT INTO `$table_name` (')
|
|
||||||
} else if node.kind == .update {
|
|
||||||
g.write('UPDATE `$table_name` SET ')
|
|
||||||
} else if node.kind == .delete {
|
|
||||||
g.write('DELETE FROM `$table_name` ')
|
|
||||||
}
|
|
||||||
if node.kind == .insert {
|
|
||||||
for i, field in node.fields {
|
|
||||||
if field.name == 'id' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
g.write('`$field.name`')
|
|
||||||
if i < node.fields.len - 1 {
|
|
||||||
g.write(', ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.write(') values (')
|
|
||||||
for i, field in node.fields {
|
|
||||||
if field.name == 'id' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
g.write('?${i + 0}')
|
|
||||||
if i < node.fields.len - 1 {
|
|
||||||
g.write(', ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.write(')')
|
|
||||||
} else if node.kind == .update {
|
|
||||||
for i, col in node.updated_columns {
|
|
||||||
g.write(' $col = ')
|
|
||||||
g.expr_to_sql(node.update_exprs[i], typ)
|
|
||||||
if i < node.updated_columns.len - 1 {
|
|
||||||
g.write(', ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.write(' WHERE ')
|
|
||||||
} else if node.kind == .delete {
|
|
||||||
g.write(' WHERE ')
|
|
||||||
}
|
|
||||||
if node.kind == .update || node.kind == .delete {
|
|
||||||
g.expr_to_sql(node.where_expr, typ)
|
|
||||||
}
|
|
||||||
g.writeln('"));')
|
|
||||||
if node.kind == .insert {
|
if node.kind == .insert {
|
||||||
// build the object now (`x.name = ... x.id == ...`)
|
// build the object now (`x.name = ... x.id == ...`)
|
||||||
for i, field in node.fields {
|
for i, field in node.fields {
|
||||||
|
@ -166,8 +127,10 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmt, typ SqlType) {
|
||||||
// insert again
|
// insert again
|
||||||
expr := node.sub_structs[int(field.typ)]
|
expr := node.sub_structs[int(field.typ)]
|
||||||
tmp_sql_stmt_name := g.sql_stmt_name
|
tmp_sql_stmt_name := g.sql_stmt_name
|
||||||
|
tmp_sql_table_name := g.sql_table_name
|
||||||
g.sql_stmt(expr)
|
g.sql_stmt(expr)
|
||||||
g.sql_stmt_name = tmp_sql_stmt_name
|
g.sql_stmt_name = tmp_sql_stmt_name
|
||||||
|
g.sql_table_name = tmp_sql_table_name
|
||||||
// get last inserted id
|
// get last inserted id
|
||||||
g.writeln('Array_sqlite__Row rows = sqlite__DB_exec($db_name, _SLIT("SELECT last_insert_rowid()")).arg0;')
|
g.writeln('Array_sqlite__Row rows = sqlite__DB_exec($db_name, _SLIT("SELECT last_insert_rowid()")).arg0;')
|
||||||
id_name := g.new_tmp_var()
|
id_name := g.new_tmp_var()
|
||||||
|
@ -204,24 +167,6 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
|
||||||
if !sub {
|
if !sub {
|
||||||
cur_line = g.go_before_stmt(0)
|
cur_line = g.go_before_stmt(0)
|
||||||
}
|
}
|
||||||
mut sql_query := 'SELECT '
|
|
||||||
table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name)
|
|
||||||
if node.is_count {
|
|
||||||
// `select count(*) from User`
|
|
||||||
sql_query += 'COUNT(*) FROM `$table_name` '
|
|
||||||
} else {
|
|
||||||
// `select id, name, country from User`
|
|
||||||
for i, field in node.fields {
|
|
||||||
sql_query += '`$field.name`'
|
|
||||||
if i < node.fields.len - 1 {
|
|
||||||
sql_query += ', '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sql_query += ' FROM `$table_name`'
|
|
||||||
}
|
|
||||||
if node.has_where {
|
|
||||||
sql_query += ' WHERE '
|
|
||||||
}
|
|
||||||
// g.write('${dbtype}__DB_q_int(*(${dbtype}__DB*)${node.db_var_name}.data, _SLIT("$sql_query')
|
// g.write('${dbtype}__DB_q_int(*(${dbtype}__DB*)${node.db_var_name}.data, _SLIT("$sql_query')
|
||||||
g.sql_stmt_name = g.new_tmp_var()
|
g.sql_stmt_name = g.new_tmp_var()
|
||||||
db_name := g.new_tmp_var()
|
db_name := g.new_tmp_var()
|
||||||
|
@ -230,33 +175,13 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
|
||||||
g.write('${c.dbtype}__DB $db_name = ') // $node.db_var_name;')
|
g.write('${c.dbtype}__DB $db_name = ') // $node.db_var_name;')
|
||||||
g.expr(node.db_expr)
|
g.expr(node.db_expr)
|
||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
|
stmt_name := g.new_tmp_var()
|
||||||
|
g.write('string $stmt_name = _SLIT("')
|
||||||
|
g.write(g.get_base_sql_select_query(node))
|
||||||
|
g.sql_expr_defaults(node, sql_typ)
|
||||||
|
g.writeln('");')
|
||||||
// g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt(*(${dbtype}__DB*)${node.db_var_name}.data, _SLIT("$sql_query')
|
// g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt(*(${dbtype}__DB*)${node.db_var_name}.data, _SLIT("$sql_query')
|
||||||
g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, _SLIT("')
|
g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, $stmt_name);')
|
||||||
g.write(sql_query)
|
|
||||||
if node.has_where && node.where_expr is ast.InfixExpr {
|
|
||||||
g.expr_to_sql(node.where_expr, sql_typ)
|
|
||||||
}
|
|
||||||
if node.has_order {
|
|
||||||
g.write(' ORDER BY ')
|
|
||||||
g.sql_side = .left
|
|
||||||
g.expr_to_sql(node.order_expr, sql_typ)
|
|
||||||
if node.has_desc {
|
|
||||||
g.write(' DESC ')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g.write(' ORDER BY id ')
|
|
||||||
}
|
|
||||||
if node.has_limit {
|
|
||||||
g.write(' LIMIT ')
|
|
||||||
g.sql_side = .right
|
|
||||||
g.expr_to_sql(node.limit_expr, sql_typ)
|
|
||||||
}
|
|
||||||
if node.has_offset {
|
|
||||||
g.write(' OFFSET ')
|
|
||||||
g.sql_side = .right
|
|
||||||
g.expr_to_sql(node.offset_expr, sql_typ)
|
|
||||||
}
|
|
||||||
g.writeln('"));')
|
|
||||||
// Dump all sql parameters generated by our custom expr handler
|
// Dump all sql parameters generated by our custom expr handler
|
||||||
binds := g.sql_buf.str()
|
binds := g.sql_buf.str()
|
||||||
g.sql_buf = strings.new_builder(100)
|
g.sql_buf = strings.new_builder(100)
|
||||||
|
@ -341,6 +266,7 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
|
||||||
tmp_sql_i := g.sql_i
|
tmp_sql_i := g.sql_i
|
||||||
tmp_sql_stmt_name := g.sql_stmt_name
|
tmp_sql_stmt_name := g.sql_stmt_name
|
||||||
tmp_sql_buf := g.sql_buf
|
tmp_sql_buf := g.sql_buf
|
||||||
|
tmp_sql_table_name := g.sql_table_name
|
||||||
|
|
||||||
g.sql_select_expr(expr, true, '\t${tmp}.$field.name =')
|
g.sql_select_expr(expr, true, '\t${tmp}.$field.name =')
|
||||||
g.writeln('//parse struct end')
|
g.writeln('//parse struct end')
|
||||||
|
@ -348,6 +274,7 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
|
||||||
g.sql_stmt_name = tmp_sql_stmt_name
|
g.sql_stmt_name = tmp_sql_stmt_name
|
||||||
g.sql_buf = tmp_sql_buf
|
g.sql_buf = tmp_sql_buf
|
||||||
g.sql_i = tmp_sql_i
|
g.sql_i = tmp_sql_i
|
||||||
|
g.sql_table_name = tmp_sql_table_name
|
||||||
} else {
|
} else {
|
||||||
g.writeln('${tmp}.$field.name = ${func}($g.sql_stmt_name, $i);')
|
g.writeln('${tmp}.$field.name = ${func}($g.sql_stmt_name, $i);')
|
||||||
}
|
}
|
||||||
|
@ -366,36 +293,525 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) sqlite3_create_table(node ast.SqlStmt, typ SqlType) {
|
fn (mut g Gen) sqlite3_create_table(node ast.SqlStmt, typ SqlType) {
|
||||||
|
g.writeln('// sqlite3 table creator')
|
||||||
|
create_string := g.table_gen(node, typ)
|
||||||
|
g.write('sqlite__DB_exec(')
|
||||||
|
g.expr(node.db_expr)
|
||||||
|
g.writeln(', _SLIT("$create_string"));')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sqlite3_bind(val string, len string, typ ast.Type) {
|
||||||
|
match g.sqlite3_type_from_v(typ) {
|
||||||
|
'INTEGER' {
|
||||||
|
g.sqlite3_bind_int(val)
|
||||||
|
}
|
||||||
|
'TEXT' {
|
||||||
|
g.sqlite3_bind_string(val, len)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
verror('bad sql type=$typ ident_name=$val')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sqlite3_bind_int(val string) {
|
||||||
|
g.sql_buf.writeln('sqlite3_bind_int($g.sql_stmt_name, $g.sql_i, $val);')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sqlite3_bind_string(val string, len string) {
|
||||||
|
g.sql_buf.writeln('sqlite3_bind_text($g.sql_stmt_name, $g.sql_i, $val, $len, 0);')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sqlite3_type_from_v(v_typ ast.Type) string {
|
||||||
|
if v_typ.is_number() || v_typ == ast.bool_type || v_typ == -1 {
|
||||||
|
return 'INTEGER'
|
||||||
|
}
|
||||||
|
if v_typ.is_string() {
|
||||||
|
return 'TEXT'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// mysql
|
||||||
|
|
||||||
|
fn (mut g Gen) mysql_stmt(node ast.SqlStmt, typ SqlType) {
|
||||||
|
g.sql_i = 0
|
||||||
|
g.writeln('\n\t//mysql insert')
|
||||||
|
db_name := g.new_tmp_var()
|
||||||
|
g.sql_stmt_name = g.new_tmp_var()
|
||||||
|
g.write('mysql__Connection $db_name = ')
|
||||||
|
g.expr(node.db_expr)
|
||||||
|
g.writeln(';')
|
||||||
|
stmt_name := g.new_tmp_var()
|
||||||
|
g.write('string $stmt_name = _SLIT("')
|
||||||
|
g.sql_defaults(node, typ)
|
||||||
|
g.writeln(';')
|
||||||
|
g.writeln('MYSQL_STMT* $g.sql_stmt_name = mysql_stmt_init(${db_name}.conn);')
|
||||||
|
g.writeln('mysql_stmt_prepare($g.sql_stmt_name, ${stmt_name}.str, ${stmt_name}.len);')
|
||||||
|
|
||||||
|
bind := g.new_tmp_var()
|
||||||
|
g.writeln('MYSQL_BIND $bind[$g.sql_i];')
|
||||||
|
g.writeln('memset($bind, 0, sizeof(MYSQL_BIND)*$g.sql_i);')
|
||||||
|
if node.kind == .insert {
|
||||||
|
for i, field in node.fields {
|
||||||
|
if field.name == 'id' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.writeln('//$field.name ($field.typ)')
|
||||||
|
x := '${node.object_var_name}.$field.name'
|
||||||
|
if g.table.type_symbols[int(field.typ)].kind == .struct_ {
|
||||||
|
// insert again
|
||||||
|
expr := node.sub_structs[int(field.typ)]
|
||||||
|
tmp_sql_stmt_name := g.sql_stmt_name
|
||||||
|
tmp_sql_table_name := g.sql_table_name
|
||||||
|
g.sql_stmt(expr)
|
||||||
|
g.sql_stmt_name = tmp_sql_stmt_name
|
||||||
|
g.sql_table_name = tmp_sql_table_name
|
||||||
|
|
||||||
|
res := g.new_tmp_var()
|
||||||
|
g.writeln('int ${res}_err = mysql_real_query(${db_name}.conn, "SELECT LAST_INSERT_ID();", 24);')
|
||||||
|
g.writeln('if (${res}_err != 0) { puts(mysql_error(${db_name}.conn)); }')
|
||||||
|
g.writeln('MYSQL_RES* $res = mysql_store_result(${db_name}.conn);')
|
||||||
|
g.writeln('if (mysql_num_rows($res) != 1) { puts("Something went wrong"); }')
|
||||||
|
g.writeln('MYSQL_ROW ${res}_row = mysql_fetch_row($res);')
|
||||||
|
g.writeln('${x}.id = string_int(tos_clone(${res}_row[0]));')
|
||||||
|
g.writeln('mysql_free_result($res);')
|
||||||
|
|
||||||
|
g.writeln('$bind[${i - 1}].buffer_type = MYSQL_TYPE_LONG;')
|
||||||
|
g.writeln('$bind[${i - 1}].buffer = &${x}.id;')
|
||||||
|
g.writeln('$bind[${i - 1}].is_null = 0;')
|
||||||
|
g.writeln('$bind[${i - 1}].length = 0;')
|
||||||
|
} else {
|
||||||
|
t, sym := g.mysql_buffer_typ_from_field(field)
|
||||||
|
g.writeln('$bind[${i - 1}].buffer_type = $t;')
|
||||||
|
if sym == 'char' {
|
||||||
|
g.writeln('$bind[${i - 1}].buffer = ($sym*) ${x}.str;')
|
||||||
|
} else {
|
||||||
|
g.writeln('$bind[${i - 1}].buffer = ($sym*) &$x;')
|
||||||
|
}
|
||||||
|
if sym == 'char' {
|
||||||
|
g.writeln('$bind[${i - 1}].buffer_length = ${x}.len;')
|
||||||
|
}
|
||||||
|
g.writeln('$bind[${i - 1}].is_null = 0;')
|
||||||
|
g.writeln('$bind[${i - 1}].length = 0;')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binds := g.sql_buf.str()
|
||||||
|
g.sql_buf = strings.new_builder(100)
|
||||||
|
g.writeln(binds)
|
||||||
|
// g.writeln('mysql_stmt_attr_set($g.sql_stmt_name, STMT_ATTR_ARRAY_SIZE, 1);')
|
||||||
|
res := g.new_tmp_var()
|
||||||
|
g.writeln('int $res = mysql_stmt_bind_param($g.sql_stmt_name, $bind);')
|
||||||
|
g.writeln('if ($res != 0) { puts(mysql_error(${db_name}.conn)); }')
|
||||||
|
g.writeln('$res = mysql_stmt_execute($g.sql_stmt_name);')
|
||||||
|
g.writeln('if ($res != 0) { puts(mysql_error(${db_name}.conn)); puts(mysql_stmt_error($g.sql_stmt_name)); }')
|
||||||
|
g.writeln('mysql_stmt_close($g.sql_stmt_name);')
|
||||||
|
g.writeln('mysql_stmt_free_result($g.sql_stmt_name);')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mysql_select_expr(node ast.SqlExpr, sub bool, line string, typ SqlType) {
|
||||||
|
g.sql_i = 0
|
||||||
|
mut cur_line := line
|
||||||
|
if !sub {
|
||||||
|
cur_line = g.go_before_stmt(0)
|
||||||
|
}
|
||||||
|
g.sql_stmt_name = g.new_tmp_var()
|
||||||
|
g.sql_bind_name = g.new_tmp_var()
|
||||||
|
db_name := g.new_tmp_var()
|
||||||
|
g.writeln('\n\t// sql select')
|
||||||
|
g.write('mysql__Connection $db_name = ')
|
||||||
|
g.expr(node.db_expr)
|
||||||
|
g.writeln(';')
|
||||||
|
|
||||||
|
stmt_name := g.new_tmp_var()
|
||||||
|
g.sql_idents = []string{}
|
||||||
|
g.sql_idents_types = []ast.Type{}
|
||||||
|
g.write('char* ${stmt_name}_raw = "')
|
||||||
|
g.write(g.get_base_sql_select_query(node))
|
||||||
|
g.sql_expr_defaults(node, typ)
|
||||||
|
g.writeln('";')
|
||||||
|
g.writeln('string $stmt_name = tos_clone(${stmt_name}_raw);')
|
||||||
|
if g.sql_idents.len > 0 {
|
||||||
|
vals := g.new_tmp_var()
|
||||||
|
g.writeln('Array_string $vals = __new_array_with_default(0, 0, sizeof(string), 0);')
|
||||||
|
for i, ident in g.sql_idents {
|
||||||
|
g.writeln('array_push(&$vals, _MOV((string[]){string_clone(_SLIT("%${i + 1}"))}));')
|
||||||
|
|
||||||
|
g.write('array_push(&$vals, _MOV((string[]){string_clone(')
|
||||||
|
if g.sql_idents_types[i] == ast.string_type {
|
||||||
|
g.write('_SLIT(')
|
||||||
|
} else {
|
||||||
|
sym := g.table.get_type_name(g.sql_idents_types[i])
|
||||||
|
g.write('${sym}_str(')
|
||||||
|
}
|
||||||
|
g.writeln('$ident))}));')
|
||||||
|
}
|
||||||
|
g.writeln('$stmt_name = string_replace_each($stmt_name, $vals);')
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
g.writeln('MYSQL_STMT* $g.sql_stmt_name = mysql_stmt_init(${db_name}.conn);')
|
||||||
|
g.writeln('mysql_stmt_prepare($g.sql_stmt_name, ${stmt_name}.str, ${stmt_name}.len);')
|
||||||
|
|
||||||
|
g.writeln('MYSQL_BIND $g.sql_bind_name[$g.sql_i];')
|
||||||
|
g.writeln('memset($g.sql_bind_name, 0, sizeof(MYSQL_BIND)*$g.sql_i);')
|
||||||
|
|
||||||
|
binds := g.sql_buf.str()
|
||||||
|
g.sql_buf = strings.new_builder(100)
|
||||||
|
g.writeln(binds)
|
||||||
|
|
||||||
|
res := g.new_tmp_var()
|
||||||
|
g.writeln('int $res = mysql_stmt_bind_param($g.sql_stmt_name, $g.sql_bind_name);')
|
||||||
|
g.writeln('if ($res != 0) { puts(mysql_error(${db_name}.conn)); }')
|
||||||
|
g.writeln('$res = mysql_stmt_execute($g.sql_stmt_name);')
|
||||||
|
g.writeln('if ($res != 0) { puts(mysql_error(${db_name}.conn)); puts(mysql_stmt_error($g.sql_stmt_name)); }')
|
||||||
|
*/
|
||||||
|
query := g.new_tmp_var()
|
||||||
|
res := g.new_tmp_var()
|
||||||
|
fields := g.new_tmp_var()
|
||||||
|
/*
|
||||||
|
g.writeln('Option_mysql__Result $res = mysql__Connection_real_query(&$db_name, $stmt_name);')
|
||||||
|
g.writeln('if (${res}.state != 0) { IError err = ${res}.err; _STR("Something went wrong\\000%.*s", 2, IError_str(err)); }')
|
||||||
|
g.writeln('Array_mysql__Row ${res}_rows = mysql__Result_rows(*(mysql__Result*)${res}.data);')*/
|
||||||
|
g.writeln('int $query = mysql_real_query(${db_name}.conn, ${stmt_name}.str, ${stmt_name}.len);')
|
||||||
|
g.writeln('if ($query != 0) { puts(mysql_error(${db_name}.conn)); }')
|
||||||
|
g.writeln('MYSQL_RES* $res = mysql_store_result(${db_name}.conn);')
|
||||||
|
g.writeln('MYSQL_ROW $fields = mysql_fetch_row($res);')
|
||||||
|
if node.is_count {
|
||||||
|
g.writeln('$cur_line string_int(tos_clone($fields[0]));')
|
||||||
|
} else {
|
||||||
|
tmp := g.new_tmp_var()
|
||||||
|
styp := g.typ(node.typ)
|
||||||
|
tmp_i := g.new_tmp_var()
|
||||||
|
mut elem_type_str := ''
|
||||||
|
g.writeln('int $tmp_i = 0;')
|
||||||
|
if node.is_array {
|
||||||
|
array_sym := g.table.get_type_symbol(node.typ)
|
||||||
|
array_info := array_sym.info as ast.Array
|
||||||
|
elem_type_str = g.typ(array_info.elem_type)
|
||||||
|
g.writeln('$styp ${tmp}_array = __new_array(0, 10, sizeof($elem_type_str));')
|
||||||
|
g.writeln('for ($tmp_i = 0; $tmp_i < mysql_num_rows($res); $tmp_i++) {')
|
||||||
|
g.writeln('\t$elem_type_str $tmp = ($elem_type_str) {')
|
||||||
|
//
|
||||||
|
sym := g.table.get_type_symbol(array_info.elem_type)
|
||||||
|
info := sym.info as ast.Struct
|
||||||
|
for i, field in info.fields {
|
||||||
|
g.zero_struct_field(field)
|
||||||
|
if i != info.fields.len - 1 {
|
||||||
|
g.write(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.writeln('};')
|
||||||
|
} else {
|
||||||
|
g.writeln('$styp $tmp = ($styp){')
|
||||||
|
// Zero fields, (only the [skip] ones?)
|
||||||
|
// If we don't, string values are going to be nil etc for fields that are not returned
|
||||||
|
// by the db engine.
|
||||||
|
sym := g.table.get_type_symbol(node.typ)
|
||||||
|
info := sym.info as ast.Struct
|
||||||
|
for i, field in info.fields {
|
||||||
|
g.zero_struct_field(field)
|
||||||
|
if i != info.fields.len - 1 {
|
||||||
|
g.write(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.writeln('};')
|
||||||
|
}
|
||||||
|
|
||||||
|
char_ptr := g.new_tmp_var()
|
||||||
|
g.writeln('char* $char_ptr = "";')
|
||||||
|
for i, field in node.fields {
|
||||||
|
g.writeln('$char_ptr = $fields[$i];')
|
||||||
|
g.writeln('if ($char_ptr == NULL) { $char_ptr = ""; }')
|
||||||
|
name := g.table.get_type_symbol(field.typ).cname
|
||||||
|
if g.table.get_type_symbol(field.typ).kind == .struct_ {
|
||||||
|
/*
|
||||||
|
id_name := g.new_tmp_var()
|
||||||
|
g.writeln('//parse struct start') //
|
||||||
|
//g.writeln('int $id_name = string_int(tos_clone($fields[$i]));')
|
||||||
|
|
||||||
|
mut expr := node.sub_structs[int(field.typ)]
|
||||||
|
mut where_expr := expr.where_expr as ast.InfixExpr
|
||||||
|
mut ident := where_expr.right as ast.Ident
|
||||||
|
|
||||||
|
ident.name = '$char_ptr[$i]'
|
||||||
|
where_expr.right = ident
|
||||||
|
expr.where_expr = where_expr
|
||||||
|
|
||||||
|
tmp_sql_i := g.sql_i
|
||||||
|
tmp_sql_stmt_name := g.sql_stmt_name
|
||||||
|
tmp_sql_buf := g.sql_buf
|
||||||
|
tmp_sql_table_name := g.sql_table_name
|
||||||
|
|
||||||
|
g.sql_select_expr(expr, true, '\t${tmp}.$field.name =')
|
||||||
|
g.writeln('//parse struct end')
|
||||||
|
|
||||||
|
g.sql_stmt_name = tmp_sql_stmt_name
|
||||||
|
g.sql_buf = tmp_sql_buf
|
||||||
|
g.sql_i = tmp_sql_i
|
||||||
|
g.sql_table_name := tmp_sql_table_name
|
||||||
|
*/
|
||||||
|
} else if field.typ == ast.string_type {
|
||||||
|
g.writeln('${tmp}.$field.name = tos_clone($char_ptr);')
|
||||||
|
} else if field.typ == ast.byte_type {
|
||||||
|
g.writeln('${tmp}.$field.name = (byte) string_${name}(tos_clone($char_ptr));')
|
||||||
|
} else if field.typ == ast.i8_type {
|
||||||
|
g.writeln('${tmp}.$field.name = (i8) string_${name}(tos_clone($char_ptr));')
|
||||||
|
} else {
|
||||||
|
g.writeln('${tmp}.$field.name = string_${name}(tos_clone($char_ptr));')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.is_array {
|
||||||
|
g.writeln('\t array_push(&${tmp}_array, _MOV(($elem_type_str[]) { $tmp }));')
|
||||||
|
g.writeln('}')
|
||||||
|
}
|
||||||
|
g.writeln('string_free(&$stmt_name);')
|
||||||
|
g.writeln('mysql_free_result($res);')
|
||||||
|
if node.is_array {
|
||||||
|
g.writeln('$cur_line ${tmp}_array; ')
|
||||||
|
} else {
|
||||||
|
g.writeln('$cur_line $tmp; ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mysql_create_table(node ast.SqlStmt, typ SqlType) {
|
||||||
|
g.writeln('// mysql table creator')
|
||||||
|
create_string := g.table_gen(node, typ)
|
||||||
|
tmp := g.new_tmp_var()
|
||||||
|
g.write('Option_mysql__Result $tmp = mysql__Connection_query(&')
|
||||||
|
g.expr(node.db_expr)
|
||||||
|
g.writeln(', _SLIT("$create_string"));')
|
||||||
|
g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; _STR("Something went wrong\\000%.*s", 2, IError_str(err)); }')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mysql_bind(val string, _ ast.Type) {
|
||||||
|
/*
|
||||||
|
t := g.mysql_buffer_typ_from_typ(typ)
|
||||||
|
mut sym := g.table.get_type_symbol(typ).cname
|
||||||
|
if typ == ast.string_type {
|
||||||
|
sym = 'char *'
|
||||||
|
}
|
||||||
|
tmp := g.new_tmp_var()
|
||||||
|
g.sql_buf.writeln('$sym $tmp = $val;')
|
||||||
|
g.sql_buf.writeln('$g.sql_bind_name[${g.sql_i - 1}].buffer_type = $t;')
|
||||||
|
g.sql_buf.writeln('$g.sql_bind_name[${g.sql_i - 1}].buffer = ($sym*) &$tmp;')
|
||||||
|
if sym == 'char *' {
|
||||||
|
g.sql_buf.writeln('$g.sql_bind_name[${g.sql_i - 1}].buffer_length = ${val}.len;')
|
||||||
|
}
|
||||||
|
g.sql_buf.writeln('$g.sql_bind_name[${g.sql_i - 1}].is_null = 0;')
|
||||||
|
g.sql_buf.writeln('$g.sql_bind_name[${g.sql_i - 1}].length = 0;')*/
|
||||||
|
g.write(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mysql_get_table_type(typ ast.Type) string {
|
||||||
|
mut table_typ := ''
|
||||||
|
match typ {
|
||||||
|
ast.i8_type, ast.byte_type, ast.bool_type {
|
||||||
|
table_typ = 'TINYINT'
|
||||||
|
}
|
||||||
|
ast.i16_type, ast.u16_type {
|
||||||
|
table_typ = 'SMALLINT'
|
||||||
|
}
|
||||||
|
ast.int_type, ast.u32_type {
|
||||||
|
table_typ = 'INT'
|
||||||
|
}
|
||||||
|
ast.i64_type, ast.u64_type {
|
||||||
|
table_typ = 'BIGINT'
|
||||||
|
}
|
||||||
|
ast.f32_type {
|
||||||
|
table_typ = 'BIGINT'
|
||||||
|
}
|
||||||
|
ast.f64_type {
|
||||||
|
table_typ = 'BIGINT'
|
||||||
|
}
|
||||||
|
ast.string_type {
|
||||||
|
table_typ = 'TEXT'
|
||||||
|
}
|
||||||
|
-1 {
|
||||||
|
table_typ = 'SERIAL'
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
return table_typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mysql_buffer_typ_from_typ(typ ast.Type) string {
|
||||||
|
mut buf_typ := ''
|
||||||
|
match typ {
|
||||||
|
ast.i8_type, ast.byte_type, ast.bool_type {
|
||||||
|
buf_typ = 'MYSQL_TYPE_TINY'
|
||||||
|
}
|
||||||
|
ast.i16_type, ast.u16_type {
|
||||||
|
buf_typ = 'MYSQL_TYPE_SHORT'
|
||||||
|
}
|
||||||
|
ast.int_type, ast.u32_type {
|
||||||
|
buf_typ = 'MYSQL_TYPE_LONG'
|
||||||
|
}
|
||||||
|
ast.i64_type, ast.u64_type {
|
||||||
|
buf_typ = 'MYSQL_TYPE_LONGLONG'
|
||||||
|
}
|
||||||
|
ast.f32_type {
|
||||||
|
buf_typ = 'MYSQL_TYPE_FLOAT'
|
||||||
|
}
|
||||||
|
ast.f64_type {
|
||||||
|
buf_typ = 'MYSQL_TYPE_DOUBLE'
|
||||||
|
}
|
||||||
|
ast.string_type {
|
||||||
|
buf_typ = 'MYSQL_TYPE_STRING'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf_typ = 'MYSQL_TYPE_NULL'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf_typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mysql_buffer_typ_from_field(field ast.StructField) (string, string) {
|
||||||
|
mut typ := g.get_sql_field_type(field)
|
||||||
|
mut sym := g.table.get_type_symbol(typ).cname
|
||||||
|
buf_typ := g.mysql_buffer_typ_from_typ(typ)
|
||||||
|
|
||||||
|
if typ == ast.string_type {
|
||||||
|
sym = 'char'
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf_typ, sym
|
||||||
|
}
|
||||||
|
|
||||||
|
// utils
|
||||||
|
|
||||||
|
fn (mut g Gen) sql_expr_defaults(node ast.SqlExpr, sql_typ SqlType) {
|
||||||
|
if node.has_where && node.where_expr is ast.InfixExpr {
|
||||||
|
g.expr_to_sql(node.where_expr, sql_typ)
|
||||||
|
}
|
||||||
|
if node.has_order {
|
||||||
|
g.write(' ORDER BY ')
|
||||||
|
g.sql_side = .left
|
||||||
|
g.expr_to_sql(node.order_expr, sql_typ)
|
||||||
|
if node.has_desc {
|
||||||
|
g.write(' DESC ')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.write(' ORDER BY id ')
|
||||||
|
}
|
||||||
|
if node.has_limit {
|
||||||
|
g.write(' LIMIT ')
|
||||||
|
g.sql_side = .right
|
||||||
|
g.expr_to_sql(node.limit_expr, sql_typ)
|
||||||
|
}
|
||||||
|
if node.has_offset {
|
||||||
|
g.write(' OFFSET ')
|
||||||
|
g.sql_side = .right
|
||||||
|
g.expr_to_sql(node.offset_expr, sql_typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) get_base_sql_select_query(node ast.SqlExpr) string {
|
||||||
|
mut sql_query := 'SELECT '
|
||||||
|
table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name)
|
||||||
|
if node.is_count {
|
||||||
|
// `select count(*) from User`
|
||||||
|
sql_query += 'COUNT(*) FROM `$table_name` '
|
||||||
|
} else {
|
||||||
|
// `select id, name, country from User`
|
||||||
|
for i, field in node.fields {
|
||||||
|
sql_query += '`$field.name`'
|
||||||
|
if i < node.fields.len - 1 {
|
||||||
|
sql_query += ', '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sql_query += ' FROM `$table_name`'
|
||||||
|
}
|
||||||
|
if node.has_where {
|
||||||
|
sql_query += ' WHERE '
|
||||||
|
}
|
||||||
|
return sql_query
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
|
||||||
|
table_name := util.strip_mod_name(g.table.get_type_symbol(node.table_expr.typ).name)
|
||||||
|
if node.kind == .insert {
|
||||||
|
g.write('INSERT INTO `$table_name` (')
|
||||||
|
} else if node.kind == .update {
|
||||||
|
g.write('UPDATE `$table_name` SET ')
|
||||||
|
} else if node.kind == .delete {
|
||||||
|
g.write('DELETE FROM `$table_name` ')
|
||||||
|
}
|
||||||
|
if node.kind == .insert {
|
||||||
|
for i, field in node.fields {
|
||||||
|
if field.name == 'id' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.write('`$field.name`')
|
||||||
|
if i < node.fields.len - 1 {
|
||||||
|
g.write(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.write(') values (')
|
||||||
|
for i, field in node.fields {
|
||||||
|
if field.name == 'id' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if typ == .sqlite3 {
|
||||||
|
g.write('?${i + 0}')
|
||||||
|
} else if typ == .mysql {
|
||||||
|
g.write('?')
|
||||||
|
}
|
||||||
|
if i < node.fields.len - 1 {
|
||||||
|
g.write(', ')
|
||||||
|
}
|
||||||
|
g.sql_i++
|
||||||
|
}
|
||||||
|
g.write(')')
|
||||||
|
} else if node.kind == .update {
|
||||||
|
for i, col in node.updated_columns {
|
||||||
|
g.write(' $col = ')
|
||||||
|
g.expr_to_sql(node.update_exprs[i], typ)
|
||||||
|
if i < node.updated_columns.len - 1 {
|
||||||
|
g.write(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.write(' WHERE ')
|
||||||
|
} else if node.kind == .delete {
|
||||||
|
g.write(' WHERE ')
|
||||||
|
}
|
||||||
|
if node.kind == .update || node.kind == .delete {
|
||||||
|
g.expr_to_sql(node.where_expr, typ)
|
||||||
|
}
|
||||||
|
g.write('")')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
|
||||||
typ_sym := g.table.get_type_symbol(node.table_expr.typ)
|
typ_sym := g.table.get_type_symbol(node.table_expr.typ)
|
||||||
if typ_sym.info !is ast.Struct {
|
if typ_sym.info !is ast.Struct {
|
||||||
verror('Type `$typ_sym.name` has to be a struct')
|
verror('Type `$typ_sym.name` has to be a struct')
|
||||||
}
|
}
|
||||||
g.writeln('// sqlite3 table creator ($typ_sym.name)')
|
|
||||||
struct_data := typ_sym.info as ast.Struct
|
struct_data := typ_sym.info as ast.Struct
|
||||||
table_name := typ_sym.name.split('.').last()
|
table_name := typ_sym.name.split('.').last()
|
||||||
mut create_string := 'CREATE TABLE IF NOT EXISTS `$table_name` ('
|
mut create_string := 'CREATE TABLE IF NOT EXISTS `$table_name` ('
|
||||||
|
|
||||||
mut fields := []string{}
|
mut fields := []string{}
|
||||||
|
|
||||||
|
mut primary := '' // for mysql
|
||||||
|
|
||||||
for field in struct_data.fields {
|
for field in struct_data.fields {
|
||||||
mut is_primary := false
|
mut is_primary := false
|
||||||
mut skip := false
|
mut no_null := false
|
||||||
for attr in field.attrs {
|
for attr in field.attrs {
|
||||||
match attr.name {
|
match attr.name {
|
||||||
'skip' {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
'primary' {
|
'primary' {
|
||||||
is_primary = true
|
is_primary = true
|
||||||
|
primary = field.name
|
||||||
|
}
|
||||||
|
'nonull' {
|
||||||
|
no_null = true
|
||||||
}
|
}
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if skip { // cpp workaround
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mut stmt := ''
|
mut stmt := ''
|
||||||
mut converted_typ := g.sql_type_from_v(typ, field.typ)
|
mut converted_typ := g.sql_type_from_v(typ, g.get_sql_field_type(field))
|
||||||
mut name := field.name
|
mut name := field.name
|
||||||
if converted_typ == '' {
|
if converted_typ == '' {
|
||||||
if g.table.get_type_symbol(field.typ).kind == .struct_ {
|
if g.table.get_type_symbol(field.typ).kind == .struct_ {
|
||||||
|
@ -417,56 +833,26 @@ fn (mut g Gen) sqlite3_create_table(node ast.SqlStmt, typ SqlType) {
|
||||||
}
|
}
|
||||||
stmt = '`$name` $converted_typ'
|
stmt = '`$name` $converted_typ'
|
||||||
|
|
||||||
if field.has_default_expr {
|
if field.has_default_expr && typ != .mysql {
|
||||||
stmt += ' DEFAULT '
|
stmt += ' DEFAULT '
|
||||||
stmt += field.default_expr.str()
|
stmt += field.default_expr.str()
|
||||||
}
|
}
|
||||||
if is_primary {
|
if no_null {
|
||||||
|
stmt += ' NOT NULL'
|
||||||
|
}
|
||||||
|
if is_primary && typ == .sqlite3 {
|
||||||
stmt += ' PRIMARY KEY'
|
stmt += ' PRIMARY KEY'
|
||||||
}
|
}
|
||||||
fields << stmt
|
fields << stmt
|
||||||
}
|
}
|
||||||
|
if typ == .mysql {
|
||||||
|
fields << 'PRIMARY KEY(`$primary`)'
|
||||||
|
}
|
||||||
create_string += fields.join(', ')
|
create_string += fields.join(', ')
|
||||||
create_string += ');'
|
create_string += ');'
|
||||||
g.write('sqlite__DB_exec(')
|
return create_string
|
||||||
g.expr(node.db_expr)
|
|
||||||
g.writeln(', _SLIT("$create_string"));')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) sqlite3_bind_int(val string) {
|
|
||||||
g.sql_buf.writeln('sqlite3_bind_int($g.sql_stmt_name, $g.sql_i, $val);')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) sqlite3_bind_string(val string, len string) {
|
|
||||||
g.sql_buf.writeln('sqlite3_bind_text($g.sql_stmt_name, $g.sql_i, $val, $len, 0);')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) sqlite3_type_from_v(typ SqlType, v_typ ast.Type) string {
|
|
||||||
if v_typ.is_number() || v_typ == ast.bool_type {
|
|
||||||
return 'INTEGER'
|
|
||||||
}
|
|
||||||
if v_typ.is_string() {
|
|
||||||
return 'TEXT'
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// mysql
|
|
||||||
|
|
||||||
fn (mut g Gen) mysql_stmt(node ast.SqlStmt) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mysql_select_expr(node ast.SqlExpr, sub bool, line string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mysql_bind_int(val string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mysql_bind_string(val string, len string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// utils
|
|
||||||
|
|
||||||
fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
|
fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
|
||||||
// Custom handling for infix exprs (since we need e.g. `and` instead of `&&` in SQL queries),
|
// Custom handling for infix exprs (since we need e.g. `and` instead of `&&` in SQL queries),
|
||||||
// strings. Everything else (like numbers, a.b) is handled by g.expr()
|
// strings. Everything else (like numbers, a.b) is handled by g.expr()
|
||||||
|
@ -497,50 +883,55 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
|
||||||
}
|
}
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
// g.write("'$it.val'")
|
// g.write("'$it.val'")
|
||||||
g.inc_sql_i()
|
g.inc_sql_i(typ)
|
||||||
g.sql_bind_string('"$expr.val"', expr.val.len.str(), typ)
|
g.sql_bind('"$expr.val"', expr.val.len.str(), g.sql_get_real_type(ast.string_type),
|
||||||
|
typ)
|
||||||
}
|
}
|
||||||
ast.IntegerLiteral {
|
ast.IntegerLiteral {
|
||||||
g.inc_sql_i()
|
g.inc_sql_i(typ)
|
||||||
g.sql_bind_int(expr.val, typ)
|
g.sql_bind(expr.val, '', g.sql_get_real_type(ast.int_type), typ)
|
||||||
}
|
}
|
||||||
ast.BoolLiteral {
|
ast.BoolLiteral {
|
||||||
// true/false literals were added to Sqlite 3.23 (2018-04-02)
|
// true/false literals were added to Sqlite 3.23 (2018-04-02)
|
||||||
// but lots of apps/distros use older sqlite (e.g. Ubuntu 18.04 LTS )
|
// but lots of apps/distros use older sqlite (e.g. Ubuntu 18.04 LTS )
|
||||||
g.inc_sql_i()
|
g.inc_sql_i(typ)
|
||||||
eval := if expr.val { '1' } else { '0' }
|
eval := if expr.val { '1' } else { '0' }
|
||||||
g.sql_bind_int(eval, typ)
|
g.sql_bind(eval, '', g.sql_get_real_type(ast.byte_type), typ)
|
||||||
}
|
}
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
// `name == user_name` => `name == ?1`
|
// `name == user_name` => `name == ?1`
|
||||||
// for left sides just add a string, for right sides, generate the bindings
|
// for left sides just add a string, for right sides, generate the bindings
|
||||||
if g.sql_side == .left {
|
if g.sql_side == .left {
|
||||||
// println("sql gen left $expr.name")
|
// println("sql gen left $expr.name")
|
||||||
|
eprintln(expr.name)
|
||||||
|
g.sql_left_type = g.get_struct_field_typ(expr.name)
|
||||||
g.write(expr.name)
|
g.write(expr.name)
|
||||||
} else {
|
} else {
|
||||||
g.inc_sql_i()
|
g.inc_sql_i(typ)
|
||||||
info := expr.info as ast.IdentVar
|
info := expr.info as ast.IdentVar
|
||||||
ityp := info.typ
|
ityp := info.typ
|
||||||
if ityp == ast.string_type {
|
if typ == .sqlite3 {
|
||||||
g.sql_bind_string('${expr.name}.str', '${expr.name}.len', typ)
|
if ityp == ast.string_type {
|
||||||
} else if ityp == ast.int_type {
|
g.sql_bind('${expr.name}.str', '${expr.name}.len', g.sql_get_real_type(ityp),
|
||||||
g.sql_bind_int(expr.name, typ)
|
typ)
|
||||||
|
} else {
|
||||||
|
g.sql_bind(expr.name, '', g.sql_get_real_type(ityp), typ)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
verror('bad sql type=$ityp ident_name=$expr.name')
|
g.sql_bind('%$g.sql_i.str()', '', g.sql_get_real_type(ityp), typ)
|
||||||
|
g.sql_idents << expr.name
|
||||||
|
g.sql_idents_types << g.sql_get_real_type(ityp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.SelectorExpr {
|
ast.SelectorExpr {
|
||||||
g.inc_sql_i()
|
g.inc_sql_i(typ)
|
||||||
if expr.typ == ast.int_type {
|
if expr.expr !is ast.Ident {
|
||||||
if expr.expr !is ast.Ident {
|
verror('orm selector not ident')
|
||||||
verror('orm selector not ident')
|
|
||||||
}
|
|
||||||
ident := expr.expr as ast.Ident
|
|
||||||
g.sql_bind_int(ident.name + '.' + expr.field_name, typ)
|
|
||||||
} else {
|
|
||||||
verror('bad sql type=$expr.typ selector expr=$expr.field_name')
|
|
||||||
}
|
}
|
||||||
|
ident := expr.expr as ast.Ident
|
||||||
|
g.sql_bind(ident.name + '.' + expr.field_name, '', g.sql_get_real_type(expr.typ),
|
||||||
|
typ)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
|
@ -554,9 +945,38 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) inc_sql_i() {
|
fn (mut g Gen) get_struct_field_typ(f string) ast.Type {
|
||||||
|
sym := g.table.get_type_symbol(g.table.type_idxs[g.sql_table_name])
|
||||||
|
|
||||||
|
mut typ := ast.Type(-1)
|
||||||
|
|
||||||
|
if sym.kind != .struct_ {
|
||||||
|
str := sym.info as ast.Struct
|
||||||
|
for field in str.fields {
|
||||||
|
if field.name != f {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
typ = g.get_sql_field_type(field)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sql_get_real_type(typ ast.Type) ast.Type {
|
||||||
|
if typ != g.sql_left_type && g.sql_left_type >= 0 {
|
||||||
|
return g.sql_left_type
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) inc_sql_i(typ SqlType) {
|
||||||
g.sql_i++
|
g.sql_i++
|
||||||
g.write('?$g.sql_i')
|
if typ == .sqlite3 {
|
||||||
|
g.write('?')
|
||||||
|
g.write('$g.sql_i')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType {
|
fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType {
|
||||||
|
@ -581,8 +1001,25 @@ fn (mut g Gen) parse_db_from_type_string(name string) SqlType {
|
||||||
'sqlite.DB' {
|
'sqlite.DB' {
|
||||||
return .sqlite3
|
return .sqlite3
|
||||||
}
|
}
|
||||||
|
'mysql.Connection' {
|
||||||
|
return .mysql
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return .unknown
|
return .unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) get_sql_field_type(field ast.StructField) ast.Type {
|
||||||
|
mut typ := field.typ
|
||||||
|
for attr in field.attrs {
|
||||||
|
if attr.name == 'sql' && attr.arg != '' {
|
||||||
|
if attr.arg.to_lower() == 'serial' {
|
||||||
|
typ = ast.Type(-1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
typ = g.table.type_idxs[attr.arg]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue