orm: add initial pg support (#9827)

pull/9876/head
Louis Schmieder 2021-04-25 17:57:55 +02:00 committed by GitHub
parent fc3b628440
commit 7184629969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 263 additions and 50 deletions

3
.gitignore vendored
View File

@ -82,3 +82,6 @@ _docs
cmd/tools/.disable_autorecompilation cmd/tools/.disable_autorecompilation
test.bin test.bin
# ignore codespace env
.venv/

View File

@ -3149,8 +3149,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 and MySQL, V has a built-in ORM (object-relational mapping) which supports SQLite, MySQL and Postgres,
but soon it will support Postgres, MS SQL, and Oracle. but soon it will support MS SQL and Oracle.
V's ORM provides a number of benefits: V's ORM provides a number of benefits:

View File

@ -1,5 +1,6 @@
import sqlite import sqlite
import mysql import mysql
import pg
[table: 'modules'] [table: 'modules']
struct Module { struct Module {
@ -46,7 +47,8 @@ fn main() {
eprintln(modul) eprintln(modul)
mysql() // mysql()
psql()
} }
fn mysql() { fn mysql() {
@ -82,3 +84,27 @@ fn mysql() {
} }
eprintln(m) eprintln(m)
} }
fn psql() {
mut db := pg.connect(host: 'localhost', user: 'test', password: 'abc', dbname: 'test') or {
panic(err)
}
mod := Module{
name: 'test'
nr_downloads: 10
creator: User{
age: 21
name: 'VUser'
is_customer: true
}
}
sql db {
create table Module
}
sql db {
insert mod into Module
}
}

View File

@ -12,7 +12,7 @@ struct Customer {
} }
fn main() { fn main() {
db := pg.connect(pg.Config{ /*db := pg.connect(pg.Config{
host: 'localhost' //'127.0.0.1' host: 'localhost' //'127.0.0.1'
user: 'postgres' user: 'postgres'
dbname: 'customerdb' dbname: 'customerdb'
@ -58,7 +58,7 @@ fn main() {
name: 'John Doe' name: 'John Doe'
nr_orders: 10 nr_orders: 10
} }
db.insert(nc) db.insert(nc)*/
} }

View File

@ -11,6 +11,8 @@ import io
// PostgreSQL Source Code // PostgreSQL Source Code
// https://doxygen.postgresql.org/libpq-fe_8h.html // https://doxygen.postgresql.org/libpq-fe_8h.html
#include <libpq-fe.h> #include <libpq-fe.h>
// for orm
#include <arpa/inet.h>
pub struct DB { pub struct DB {
mut: mut:
@ -59,7 +61,7 @@ fn C.PQexecParams(conn voidptr, command byteptr, nParams int, paramTypes int, pa
fn C.PQputCopyData(conn voidptr, buffer byteptr, nbytes int) int fn C.PQputCopyData(conn voidptr, buffer byteptr, nbytes int) int
fn C.PQputCopyEnd(voidptr, int) int fn C.PQputCopyEnd(voidptr, &byte) int
fn C.PQgetCopyData(conn voidptr, buffer &byteptr, async int) int fn C.PQgetCopyData(conn voidptr, buffer &byteptr, async int) int

View File

@ -40,6 +40,9 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
.mysql { .mysql {
g.mysql_stmt(node, typ) g.mysql_stmt(node, typ)
} }
.psql {
g.psql_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
} }
@ -55,6 +58,9 @@ fn (mut g Gen) sql_create_table(node ast.SqlStmt) {
.mysql { .mysql {
g.mysql_create_table(node, typ) g.mysql_create_table(node, typ)
} }
.psql {
g.psql_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
} }
@ -70,6 +76,9 @@ fn (mut g Gen) sql_drop_table(node ast.SqlStmt) {
.mysql { .mysql {
g.mysql_drop_table(node, typ) g.mysql_drop_table(node, typ)
} }
.psql {
g.psql_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
} }
@ -92,7 +101,7 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) {
} }
} }
fn (mut g Gen) sql_bind(val string, len string, real_type ast.Type, typ SqlType) { fn (mut g Gen) sql_bind(val string, len string, real_type ast.Type, typ SqlType, data []string) {
match typ { match typ {
.sqlite3 { .sqlite3 {
g.sqlite3_bind(val, len, real_type) g.sqlite3_bind(val, len, real_type)
@ -100,6 +109,9 @@ fn (mut g Gen) sql_bind(val string, len string, real_type ast.Type, typ SqlType)
.mysql { .mysql {
g.mysql_bind(val, real_type) g.mysql_bind(val, real_type)
} }
.psql {
g.psql_bind(val, data, real_type)
}
else {} else {}
} }
} }
@ -112,6 +124,9 @@ fn (mut g Gen) sql_type_from_v(typ SqlType, v_typ ast.Type) string {
.mysql { .mysql {
return g.mysql_get_table_type(v_typ) return g.mysql_get_table_type(v_typ)
} }
.psql {
return g.psql_get_table_type(v_typ)
}
else { else {
// add error // add error
} }
@ -141,7 +156,7 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmt, typ SqlType) {
x := '${node.object_var_name}.$field.name' x := '${node.object_var_name}.$field.name'
if field.typ == ast.string_type { if field.typ == ast.string_type {
g.writeln('sqlite3_bind_text($g.sql_stmt_name, ${i + 0}, (char*)${x}.str, ${x}.len, 0);') g.writeln('sqlite3_bind_text($g.sql_stmt_name, ${i + 0}, (char*)${x}.str, ${x}.len, 0);')
} else if g.table.type_symbols[int(field.typ)].kind == .struct_ { } else if g.table.get_type_symbol(field.typ).kind == .struct_ {
// 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
@ -271,7 +286,7 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
g.writeln('if ($string_data != NULL) {') g.writeln('if ($string_data != NULL) {')
g.writeln('\t${tmp}.$field.name = tos_clone($string_data);') g.writeln('\t${tmp}.$field.name = tos_clone($string_data);')
g.writeln('}') g.writeln('}')
} else if g.table.type_symbols[int(field.typ)].kind == .struct_ { } else if g.table.get_type_symbol(field.typ).kind == .struct_ {
id_name := g.new_tmp_var() id_name := g.new_tmp_var()
g.writeln('//parse struct start') g.writeln('//parse struct start')
g.writeln('int $id_name = ${func}($g.sql_stmt_name, $i);') g.writeln('int $id_name = ${func}($g.sql_stmt_name, $i);')
@ -322,10 +337,10 @@ fn (mut g Gen) sqlite3_create_table(node ast.SqlStmt, typ SqlType) {
fn (mut g Gen) sqlite3_drop_table(node ast.SqlStmt, typ SqlType) { fn (mut g Gen) sqlite3_drop_table(node ast.SqlStmt, typ SqlType) {
table_name := g.get_table_name(node.table_expr) table_name := g.get_table_name(node.table_expr)
g.writeln('// sqlite3 table drop') g.writeln('// sqlite3 table drop')
create_string := 'DROP TABLE $table_name;' drop_string := 'DROP TABLE `$table_name`;'
g.write('sqlite__DB_exec(') g.write('sqlite__DB_exec(')
g.expr(node.db_expr) g.expr(node.db_expr)
g.writeln(', _SLIT("$create_string"));') g.writeln(', _SLIT("$drop_string"));')
} }
fn (mut g Gen) sqlite3_bind(val string, len string, typ ast.Type) { fn (mut g Gen) sqlite3_bind(val string, len string, typ ast.Type) {
@ -387,7 +402,7 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmt, typ SqlType) {
} }
g.writeln('//$field.name ($field.typ)') g.writeln('//$field.name ($field.typ)')
x := '${node.object_var_name}.$field.name' x := '${node.object_var_name}.$field.name'
if g.table.type_symbols[int(field.typ)].kind == .struct_ { if g.table.get_type_symbol(field.typ).kind == .struct_ {
// 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
@ -616,11 +631,11 @@ fn (mut g Gen) mysql_create_table(node ast.SqlStmt, typ SqlType) {
fn (mut g Gen) mysql_drop_table(node ast.SqlStmt, typ SqlType) { fn (mut g Gen) mysql_drop_table(node ast.SqlStmt, typ SqlType) {
table_name := g.get_table_name(node.table_expr) table_name := g.get_table_name(node.table_expr)
g.writeln('// mysql table drop') g.writeln('// mysql table drop')
create_string := 'DROP TABLE $table_name;' drop_string := 'DROP TABLE `$table_name`;'
tmp := g.new_tmp_var() tmp := g.new_tmp_var()
g.write('Option_mysql__Result $tmp = mysql__Connection_query(&') g.write('Option_mysql__Result $tmp = mysql__Connection_query(&')
g.expr(node.db_expr) g.expr(node.db_expr)
g.writeln(', _SLIT("$create_string"));') g.writeln(', _SLIT("$drop_string"));')
g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }') g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
} }
@ -659,10 +674,10 @@ fn (mut g Gen) mysql_get_table_type(typ ast.Type) string {
table_typ = 'BIGINT' table_typ = 'BIGINT'
} }
ast.f32_type { ast.f32_type {
table_typ = 'BIGINT' table_typ = 'FLOAT'
} }
ast.f64_type { ast.f64_type {
table_typ = 'BIGINT' table_typ = 'DOUBLE'
} }
ast.string_type { ast.string_type {
table_typ = 'TEXT' table_typ = 'TEXT'
@ -718,16 +733,162 @@ fn (mut g Gen) mysql_buffer_typ_from_field(field ast.StructField) (string, strin
return buf_typ, sym return buf_typ, sym
} }
// psql
fn (mut g Gen) psql_stmt(node ast.SqlStmt, typ SqlType) {
g.sql_i = 0
g.sql_idents = []string{}
param_values := g.new_tmp_var()
param_lens := g.new_tmp_var()
param_formats := g.new_tmp_var()
g.writeln('\n\t//psql insert')
db_name := g.new_tmp_var()
g.sql_stmt_name = g.new_tmp_var()
g.write('pg__DB $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, param_values, param_lens, param_formats)
g.writeln(';')
g.writeln('char *$param_values[$g.sql_i]; //param values')
g.writeln('int $param_lens[$g.sql_i]; //param lens')
g.writeln('int $param_formats[$g.sql_i]; // param formats')
if node.kind == .insert {
for i, field in node.fields {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
g.writeln('//$field.name ($field.typ)')
x := '${node.object_var_name}.$field.name'
field_type := g.get_sql_field_type(field)
if g.table.get_type_symbol(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.write('Option_int $res = pg__DB_q_int($db_name, _SLIT("SELECT LASTVAL();"));')
g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln(_STR("\\000%.*s", 2, IError_str(err))); }')
g.sql_buf = strings.new_builder(100)
g.sql_bind('${res}.data', '', ast.int_type, typ, [
(i - 1).str(),
param_values,
param_lens,
param_formats,
])
g.writeln(g.sql_buf.str())
} else {
g.sql_buf = strings.new_builder(100)
g.sql_bind(x, '', field_type, typ, [(i - 1).str(), param_values, param_lens,
param_formats,
])
g.writeln(g.sql_buf.str())
}
}
}
binds := g.sql_buf.str()
g.sql_buf = strings.new_builder(100)
g.writeln(binds)
res := g.new_tmp_var()
g.writeln('PGresult * $res = PQexecParams(${db_name}.conn, ${stmt_name}.str, $g.sql_i, 0, $param_values, $param_lens, $param_formats, 0);')
g.writeln('Option_Array_pg__Row ${res}_rows = pg__DB_handle_error_or_result($db_name, $res, _SLIT("$stmt_name"));')
g.writeln('if (${res}_rows.state != 0) { IError err = ${res}_rows.err; eprintln(_STR("\\000%.*s", 2, IError_str(err))); }')
}
fn (mut g Gen) psql_create_table(node ast.SqlStmt, typ SqlType) {
g.writeln('// psql table creator')
create_string := g.table_gen(node, typ)
tmp := g.new_tmp_var()
g.write('Option_Array_pg__Row $tmp = pg__DB_exec(')
g.expr(node.db_expr)
g.writeln(', _SLIT("$create_string"));')
g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
}
fn (mut g Gen) psql_drop_table(node ast.SqlStmt, typ SqlType) {
table_name := g.get_table_name(node.table_expr)
g.writeln('// psql table drop')
drop_string := 'DROP TABLE "$table_name";'
tmp := g.new_tmp_var()
g.write('Option_Array_pg__Row $tmp = pg__DB_exec(&')
g.expr(node.db_expr)
g.writeln(', _SLIT("$drop_string"));')
g.writeln('if (${tmp}.state != 0) { IError err = ${tmp}.err; eprintln(_STR("Something went wrong\\000%.*s", 2, IError_str(err))); }')
}
fn (mut g Gen) psql_get_table_type(typ ast.Type) string {
mut table_typ := ''
match typ {
ast.i8_type, ast.byte_type, ast.bool_type {
table_typ = 'CHAR(1)'
}
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 = 'FLOAT4'
}
ast.f64_type {
table_typ = 'FLOAT8'
}
ast.string_type {
table_typ = 'TEXT'
}
-1 {
table_typ = 'SERIAL'
}
else {}
}
return table_typ
}
fn (mut g Gen) psql_bind(val string, data []string, typ ast.Type) {
tmp := g.new_tmp_var()
g.sql_idents << tmp
g.sql_buf.write_string('char* $tmp = ')
if typ.is_number() {
g.sql_buf.writeln('(char *) htonl($val);')
g.sql_buf.writeln('\t${data[1]}[${data[0]}] = &$tmp;')
g.sql_buf.writeln('\t${data[2]}[${data[0]}] = sizeof($tmp);')
g.sql_buf.writeln('\t${data[3]}[${data[0]}] = 1;')
} else if typ.is_string() {
g.sql_buf.writeln('(char *) ${val}.str;')
g.sql_buf.writeln('\t${data[1]}[${data[0]}] = &$tmp;')
g.sql_buf.writeln('\t${data[2]}[${data[0]}] = 0;')
g.sql_buf.writeln('\t${data[3]}[${data[0]}] = 0;')
} else {
g.sql_buf.writeln('(char *) htonl(0);')
g.sql_buf.writeln('if ($val) { $tmp = (char *) htonl(1); }')
g.sql_buf.writeln('\t${data[1]}[${data[0]}] = &$tmp;')
g.sql_buf.writeln('\t${data[2]}[${data[0]}] = sizeof($tmp);')
g.sql_buf.writeln('\t${data[3]}[${data[0]}] = 1;')
}
}
// utils // utils
fn (mut g Gen) sql_expr_defaults(node ast.SqlExpr, sql_typ SqlType) { fn (mut g Gen) sql_expr_defaults(node ast.SqlExpr, sql_typ SqlType) {
if node.has_where && node.where_expr is ast.InfixExpr { if node.has_where && node.where_expr is ast.InfixExpr {
g.expr_to_sql(node.where_expr, sql_typ) g.expr_to_sql(node.where_expr, sql_typ, [])
} }
if node.has_order { if node.has_order {
g.write(' ORDER BY ') g.write(' ORDER BY ')
g.sql_side = .left g.sql_side = .left
g.expr_to_sql(node.order_expr, sql_typ) g.expr_to_sql(node.order_expr, sql_typ, [])
if node.has_desc { if node.has_desc {
g.write(' DESC ') g.write(' DESC ')
} }
@ -737,12 +898,12 @@ fn (mut g Gen) sql_expr_defaults(node ast.SqlExpr, sql_typ SqlType) {
if node.has_limit { if node.has_limit {
g.write(' LIMIT ') g.write(' LIMIT ')
g.sql_side = .right g.sql_side = .right
g.expr_to_sql(node.limit_expr, sql_typ) g.expr_to_sql(node.limit_expr, sql_typ, [])
} }
if node.has_offset { if node.has_offset {
g.write(' OFFSET ') g.write(' OFFSET ')
g.sql_side = .right g.sql_side = .right
g.expr_to_sql(node.offset_expr, sql_typ) g.expr_to_sql(node.offset_expr, sql_typ, [])
} }
} }
@ -768,21 +929,25 @@ fn (mut g Gen) get_base_sql_select_query(node ast.SqlExpr) string {
return sql_query return sql_query
} }
fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) { fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType, psql_data ...string) {
table_name := g.get_table_name(node.table_expr) table_name := g.get_table_name(node.table_expr)
mut lit := '`'
if typ == .psql {
lit = '\\"'
}
if node.kind == .insert { if node.kind == .insert {
g.write('INSERT INTO `$table_name` (') g.write('INSERT INTO $lit$table_name$lit (')
} else if node.kind == .update { } else if node.kind == .update {
g.write('UPDATE `$table_name` SET ') g.write('UPDATE $lit$table_name$lit SET ')
} else if node.kind == .delete { } else if node.kind == .delete {
g.write('DELETE FROM `$table_name` ') g.write('DELETE FROM $lit$table_name$lit ')
} }
if node.kind == .insert { if node.kind == .insert {
for i, field in node.fields { for i, field in node.fields {
if g.get_sql_field_type(field) == ast.Type(-1) { if g.get_sql_field_type(field) == ast.Type(-1) {
continue continue
} }
g.write('`${g.get_field_name(field)}`') g.write('$lit${g.get_field_name(field)}$lit')
if i < node.fields.len - 1 { if i < node.fields.len - 1 {
g.write(', ') g.write(', ')
} }
@ -792,21 +957,16 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
if g.get_sql_field_type(field) == ast.Type(-1) { if g.get_sql_field_type(field) == ast.Type(-1) {
continue continue
} }
if typ == .sqlite3 { g.inc_sql_i(typ)
g.write('?${i + 0}')
} else if typ == .mysql {
g.write('?')
}
if i < node.fields.len - 1 { if i < node.fields.len - 1 {
g.write(', ') g.write(', ')
} }
g.sql_i++
} }
g.write(')') g.write(')')
} else if node.kind == .update { } else if node.kind == .update {
for i, col in node.updated_columns { for i, col in node.updated_columns {
g.write(' ${g.get_field_name(g.get_struct_field(col))} = ') g.write(' ${g.get_field_name(g.get_struct_field(col))} = ')
g.expr_to_sql(node.update_exprs[i], typ) g.expr_to_sql(node.update_exprs[i], typ, psql_data)
if i < node.updated_columns.len - 1 { if i < node.updated_columns.len - 1 {
g.write(', ') g.write(', ')
} }
@ -816,16 +976,20 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmt, typ SqlType) {
g.write(' WHERE ') g.write(' WHERE ')
} }
if node.kind == .update || node.kind == .delete { if node.kind == .update || node.kind == .delete {
g.expr_to_sql(node.where_expr, typ) g.expr_to_sql(node.where_expr, typ, psql_data)
} }
g.write('")') g.write(';")')
} }
fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string { 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)
struct_data := typ_sym.struct_info() struct_data := typ_sym.struct_info()
table_name := g.get_table_name(node.table_expr) table_name := g.get_table_name(node.table_expr)
mut create_string := 'CREATE TABLE IF NOT EXISTS `$table_name` (' mut lit := '`'
if typ == .psql {
lit = '\\"'
}
mut create_string := 'CREATE TABLE IF NOT EXISTS $lit$table_name$lit ('
mut fields := []string{} mut fields := []string{}
@ -882,7 +1046,7 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
continue continue
} }
} }
stmt = '`$name` $converted_typ' stmt = '$lit$name$lit $converted_typ'
if field.has_default_expr && typ != .mysql { if field.has_default_expr && typ != .mysql {
stmt += ' DEFAULT ' stmt += ' DEFAULT '
@ -903,20 +1067,20 @@ fn (mut g Gen) table_gen(node ast.SqlStmt, typ SqlType) string {
for k, v in unique { for k, v in unique {
mut tmp := []string{} mut tmp := []string{}
for f in v { for f in v {
tmp << '`$f`' tmp << '$lit$f$lit'
} }
fields << '/* $k */UNIQUE(${tmp.join(', ')})' fields << '/* $k */UNIQUE(${tmp.join(', ')})'
} }
} }
if typ == .mysql { if typ == .mysql {
fields << 'PRIMARY KEY(`$primary`)' fields << 'PRIMARY KEY($lit$primary$lit)'
} }
create_string += fields.join(', ') create_string += fields.join(', ')
create_string += ');' create_string += ');'
return create_string return create_string
} }
fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) { fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType, psql_data []string) {
// 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()
// //
@ -925,7 +1089,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
match expr { match expr {
ast.InfixExpr { ast.InfixExpr {
g.sql_side = .left g.sql_side = .left
g.expr_to_sql(expr.left, typ) g.expr_to_sql(expr.left, typ, psql_data)
match expr.op { match expr.op {
.ne { g.write(' != ') } .ne { g.write(' != ') }
.eq { g.write(' = ') } .eq { g.write(' = ') }
@ -942,24 +1106,26 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
else {} else {}
} }
g.sql_side = .right g.sql_side = .right
g.expr_to_sql(expr.right, typ) g.expr_to_sql(expr.right, typ, psql_data)
} }
ast.StringLiteral { ast.StringLiteral {
// g.write("'$it.val'") // g.write("'$it.val'")
g.inc_sql_i(typ) g.inc_sql_i(typ)
g.sql_bind('"$expr.val"', expr.val.len.str(), g.sql_get_real_type(ast.string_type), g.sql_bind('"$expr.val"', expr.val.len.str(), g.sql_get_real_type(ast.string_type),
typ) typ, g.append_arr(g.sql_i, psql_data))
} }
ast.IntegerLiteral { ast.IntegerLiteral {
g.inc_sql_i(typ) g.inc_sql_i(typ)
g.sql_bind(expr.val, '', g.sql_get_real_type(ast.int_type), typ) g.sql_bind(expr.val, '', g.sql_get_real_type(ast.int_type), typ, g.append_arr(g.sql_i,
psql_data))
} }
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(typ) g.inc_sql_i(typ)
eval := if expr.val { '1' } else { '0' } eval := if expr.val { '1' } else { '0' }
g.sql_bind(eval, '', g.sql_get_real_type(ast.byte_type), typ) g.sql_bind(eval, '', g.sql_get_real_type(ast.byte_type), typ, g.append_arr(g.sql_i,
psql_data))
} }
ast.Ident { ast.Ident {
// `name == user_name` => `name == ?1` // `name == user_name` => `name == ?1`
@ -975,12 +1141,14 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
if typ == .sqlite3 { if typ == .sqlite3 {
if ityp == ast.string_type { if ityp == ast.string_type {
g.sql_bind('${expr.name}.str', '${expr.name}.len', g.sql_get_real_type(ityp), g.sql_bind('${expr.name}.str', '${expr.name}.len', g.sql_get_real_type(ityp),
typ) typ, g.append_arr(g.sql_i, psql_data))
} else { } else {
g.sql_bind(expr.name, '', g.sql_get_real_type(ityp), typ) g.sql_bind(expr.name, '', g.sql_get_real_type(ityp), typ, g.append_arr(g.sql_i,
psql_data))
} }
} else { } else {
g.sql_bind('%$g.sql_i.str()', '', g.sql_get_real_type(ityp), typ) g.sql_bind('%$g.sql_i.str()', '', g.sql_get_real_type(ityp), typ,
g.append_arr(g.sql_i, psql_data))
g.sql_idents << expr.name g.sql_idents << expr.name
g.sql_idents_types << g.sql_get_real_type(ityp) g.sql_idents_types << g.sql_get_real_type(ityp)
} }
@ -993,7 +1161,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
} }
ident := expr.expr as ast.Ident ident := expr.expr as ast.Ident
g.sql_bind(ident.name + '.' + expr.field_name, '', g.sql_get_real_type(expr.typ), g.sql_bind(ident.name + '.' + expr.field_name, '', g.sql_get_real_type(expr.typ),
typ) typ, g.append_arr(g.sql_i, psql_data))
} }
else { else {
g.expr(expr) g.expr(expr)
@ -1007,6 +1175,13 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr, typ SqlType) {
*/ */
} }
fn (mut g Gen) append_arr(i int, arr []string) []string {
mut tmp := []string{}
tmp << i.str()
tmp << arr
return tmp
}
fn (mut g Gen) get_struct_field_typ(f string) ast.Type { 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]) sym := g.table.get_type_symbol(g.table.type_idxs[g.sql_table_name])
@ -1035,8 +1210,12 @@ fn (mut g Gen) sql_get_real_type(typ ast.Type) ast.Type {
fn (mut g Gen) inc_sql_i(typ SqlType) { fn (mut g Gen) inc_sql_i(typ SqlType) {
g.sql_i++ g.sql_i++
if typ == .sqlite3 { if typ == .psql {
g.write('?') g.write('$')
} else {
g.write('?') // used in sqlite `?i` and mysql `?`
}
if typ != .mysql {
g.write('$g.sql_i') g.write('$g.sql_i')
} }
} }
@ -1066,6 +1245,9 @@ fn (mut g Gen) parse_db_from_type_string(name string) SqlType {
'mysql.Connection' { 'mysql.Connection' {
return .mysql return .mysql
} }
'pg.DB' {
return .psql
}
else { else {
return .unknown return .unknown
} }