orm: support arrays (#9936)

pull/9940/head
Louis Schmieder 2021-04-30 08:13:26 +02:00 committed by GitHub
parent b15156d465
commit fb685eee18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 434 additions and 15 deletions

View File

@ -18,12 +18,145 @@ struct User {
skipped_string string [skip]
}
struct Parent {
id int [primary; sql: serial]
name string
chields []Chield [fkey: 'parent_id']
}
struct Chield {
id int [primary; sql: serial]
parent_id int
name string
}
fn main() {
sqlite3_array()
mysql_array()
psql_array()
sqlite3()
mysql()
psql()
}
fn sqlite3_array() {
mut db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table Parent
}
par := Parent{
name: 'test'
chields: [
Chield{
name: 'abc'
},
Chield{
name: 'def'
},
]
}
sql db {
insert par into Parent
}
parent := sql db {
select from Parent where id == 1
}
sql db {
drop table Chield
drop table Parent
}
eprintln(parent)
}
fn mysql_array() {
mut db := mysql.Connection{
host: 'localhost'
port: 3306
username: 'root'
password: 'abc'
dbname: 'test'
}
db.connect() or { panic(err) }
sql db {
create table Parent
}
par := Parent{
name: 'test'
chields: [
Chield{
name: 'abc'
},
Chield{
name: 'def'
},
]
}
sql db {
insert par into Parent
}
parent := sql db {
select from Parent where id == 1
}
eprintln(parent)
sql db {
drop table Chield
drop table Parent
}
db.close()
}
fn psql_array() {
mut db := pg.connect(host: 'localhost', user: 'test', password: 'abc', dbname: 'test') or {
panic(err)
}
sql db {
create table Parent
}
par := Parent{
name: 'test'
chields: [
Chield{
name: 'abc'
},
Chield{
name: 'def'
},
]
}
sql db {
insert par into Parent
}
parent := sql db {
select from Parent where id == 1
}
eprintln(parent)
sql db {
drop table Chield
drop table Parent
}
db.close()
}
fn sqlite3() {
mut db := sqlite.connect(':memory:') or { panic(err) }
sql db {

View File

@ -6439,15 +6439,24 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
info := sym.info as ast.Struct
fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, sym.name)
mut sub_structs := map[int]ast.SqlExpr{}
for f in fields.filter(c.table.type_symbols[int(it.typ)].kind == .struct_) {
for f in fields.filter(c.table.type_symbols[int(it.typ)].kind == .struct_
|| (c.table.get_type_symbol(it.typ).kind == .array
&& c.table.get_type_symbol(c.table.get_type_symbol(it.typ).array_info().elem_type).kind == .struct_)) {
typ := if c.table.get_type_symbol(f.typ).kind == .struct_ {
f.typ
} else if c.table.get_type_symbol(f.typ).kind == .array {
c.table.get_type_symbol(f.typ).array_info().elem_type
} else {
ast.Type(0)
}
mut n := ast.SqlExpr{
pos: node.pos
has_where: true
typ: f.typ
typ: typ
db_expr: node.db_expr
table_expr: ast.TypeNode{
pos: node.table_expr.pos
typ: f.typ
typ: typ
}
}
tmp_inside_sql := c.inside_sql
@ -6484,7 +6493,7 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
or_block: ast.OrExpr{}
}
sub_structs[int(f.typ)] = n
sub_structs[int(typ)] = n
}
node.fields = fields
node.sub_structs = sub_structs.move()
@ -6531,20 +6540,29 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
info := table_sym.info as ast.Struct
fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
mut sub_structs := map[int]ast.SqlStmtLine{}
for f in fields.filter(c.table.type_symbols[int(it.typ)].kind == .struct_) {
for f in fields.filter((c.table.type_symbols[int(it.typ)].kind == .struct_)
|| (c.table.get_type_symbol(it.typ).kind == .array
&& c.table.get_type_symbol(c.table.get_type_symbol(it.typ).array_info().elem_type).kind == .struct_)) {
typ := if c.table.get_type_symbol(f.typ).kind == .struct_ {
f.typ
} else if c.table.get_type_symbol(f.typ).kind == .array {
c.table.get_type_symbol(f.typ).array_info().elem_type
} else {
ast.Type(0)
}
mut n := ast.SqlStmtLine{
pos: node.pos
kind: node.kind
table_expr: ast.TypeNode{
pos: node.table_expr.pos
typ: f.typ
typ: typ
}
object_var_name: '${node.object_var_name}.$f.name'
}
tmp_inside_sql := c.inside_sql
c.sql_stmt_line(mut n)
c.inside_sql = tmp_inside_sql
sub_structs[int(f.typ)] = n
sub_structs[typ] = n
}
node.fields = fields
node.sub_structs = sub_structs.move()
@ -6562,7 +6580,10 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
fn (mut c Checker) fetch_and_verify_orm_fields(info ast.Struct, pos token.Position, table_name string) []ast.StructField {
fields := info.fields.filter((it.typ in [ast.string_type, ast.int_type, ast.bool_type]
|| c.table.type_symbols[int(it.typ)].kind == .struct_) && !it.attrs.contains('skip'))
|| c.table.type_symbols[int(it.typ)].kind == .struct_
|| (c.table.get_type_symbol(it.typ).kind == .array
&& c.table.get_type_symbol(c.table.get_type_symbol(it.typ).array_info().elem_type).kind == .struct_))
&& !it.attrs.contains('skip'))
if fields.len == 0 {
c.error('V orm: select: empty fields in `$table_name`', pos)
return []ast.StructField{}

View File

@ -121,6 +121,8 @@ mut:
sql_idents_types []ast.Type
sql_left_type ast.Type
sql_table_name string
sql_fkey string
sql_parent_id string
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
inside_vweb_tmpl bool
inside_return bool

View File

@ -157,12 +157,18 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr)
g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, _SLIT("')
g.sql_defaults(node, typ)
g.writeln(');')
mut arr_stmt := []ast.SqlStmtLine{}
mut arr_fkeys := []string{}
if node.kind == .insert {
// build the object now (`x.name = ... x.id == ...`)
for i, field in node.fields {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
if field.name == g.sql_fkey && g.sql_fkey != '' {
g.writeln('sqlite3_bind_int($g.sql_stmt_name, ${i + 0} , $g.sql_parent_id); // parent id')
continue
}
x := '${node.object_var_name}.$field.name'
if field.typ == ast.string_type {
g.writeln('sqlite3_bind_text($g.sql_stmt_name, ${i + 0}, (char*)${x}.str, ${x}.len, 0);')
@ -180,6 +186,24 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr)
id_name := g.new_tmp_var()
g.writeln('int $id_name = string_int((*(string*)array_get((*(sqlite__Row*)array_get($res, 0)).vals, 0)));')
g.writeln('sqlite3_bind_int($g.sql_stmt_name, ${i + 0} , $id_name); // id')
} else if g.table.get_type_symbol(field.typ).kind == .array {
t := g.table.get_type_symbol(field.typ).array_info().elem_type
if g.table.get_type_symbol(t).kind == .struct_ {
mut fkey := ''
for attr in field.attrs {
if attr.name == 'fkey' && attr.arg != '' && attr.kind == .string {
fkey = attr.arg
break
}
}
if fkey == '' {
verror('fkey attribute has to be set for arrays in orm')
continue
}
arr_stmt << node.sub_structs[int(t)]
arr_fkeys << fkey
}
} else {
g.writeln('sqlite3_bind_int($g.sql_stmt_name, ${i + 0} , $x); // stmt')
}
@ -193,6 +217,15 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr)
g.writeln('\tint $step_res = sqlite3_step($g.sql_stmt_name);')
g.writeln('\tif( ($step_res != SQLITE_OK) && ($step_res != SQLITE_DONE)){ puts(sqlite3_errmsg(${db_name}.conn)); }')
g.writeln('\tsqlite3_finalize($g.sql_stmt_name);')
if arr_stmt.len > 0 {
res := g.new_tmp_var()
g.writeln('Array_sqlite__Row $res = sqlite__DB_exec($db_name, _SLIT("SELECT last_insert_rowid()")).arg0;')
id_name := g.new_tmp_var()
g.writeln('int $id_name = string_int((*(string*)array_get((*(sqlite__Row*)array_get($res, 0)).vals, 0)));')
g.sql_arr_stmt(arr_stmt, arr_fkeys, id_name, db_expr)
}
}
fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_typ SqlType) {
@ -287,7 +320,14 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
// g.writeln('printf("RES: %d\\n", _step_res$tmp) ;')
g.writeln('\tif (_step_res$tmp == SQLITE_OK || _step_res$tmp == SQLITE_ROW) {')
}
mut primary := ''
for i, field in node.fields {
for attr in field.attrs {
if attr.name == 'primary' {
primary = '${tmp}.$field.name'
break
}
}
mut func := 'sqlite3_column_int'
if field.typ == ast.string_type {
func = 'sqlite3_column_text'
@ -319,6 +359,8 @@ fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_
g.sql_buf = tmp_sql_buf
g.sql_i = tmp_sql_i
g.sql_table_name = tmp_sql_table_name
} else if g.table.get_type_symbol(field.typ).kind == .array {
g.sql_select_arr(field, node, primary, tmp)
} else {
g.writeln('${tmp}.$field.name = ${func}($g.sql_stmt_name, $i);')
}
@ -405,11 +447,28 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
bind := g.new_tmp_var()
g.writeln('MYSQL_BIND $bind[$g.sql_i];')
g.writeln('memset($bind, 0, sizeof(MYSQL_BIND)*$g.sql_i);')
mut arr_stmt := []ast.SqlStmtLine{}
mut arr_fkeys := []string{}
if node.kind == .insert {
for i, field in node.fields {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
if field.name == g.sql_fkey && g.sql_fkey != '' {
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*) ${g.sql_parent_id}.str;')
} else {
g.writeln('$bind[${i - 1}].buffer = ($sym*) &$g.sql_parent_id;')
}
if sym == 'char' {
g.writeln('$bind[${i - 1}].buffer_length = ${g.sql_parent_id}.len;')
}
g.writeln('$bind[${i - 1}].is_null = 0;')
g.writeln('$bind[${i - 1}].length = 0;')
continue
}
g.writeln('//$field.name ($field.typ)')
x := '${node.object_var_name}.$field.name'
if g.table.get_type_symbol(field.typ).kind == .struct_ {
@ -434,6 +493,24 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
g.writeln('$bind[${i - 1}].buffer = &${x}.id;')
g.writeln('$bind[${i - 1}].is_null = 0;')
g.writeln('$bind[${i - 1}].length = 0;')
} else if g.table.get_type_symbol(field.typ).kind == .array {
t := g.table.get_type_symbol(field.typ).array_info().elem_type
if g.table.get_type_symbol(t).kind == .struct_ {
mut fkey := ''
for attr in field.attrs {
if attr.name == 'fkey' && attr.arg != '' && attr.kind == .string {
fkey = attr.arg
break
}
}
if fkey == '' {
verror('fkey attribute has to be set for arrays in orm')
continue
}
arr_stmt << node.sub_structs[int(t)]
arr_fkeys << fkey
}
} else {
t, sym := g.mysql_buffer_typ_from_field(field)
g.writeln('$bind[${i - 1}].buffer_type = $t;')
@ -460,6 +537,20 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
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);')
if arr_stmt.len > 0 {
rs := g.new_tmp_var()
g.writeln('int ${rs}_err = mysql_real_query(${db_name}.conn, "SELECT LAST_INSERT_ID();", 24);')
g.writeln('if (${rs}_err != 0) { puts(mysql_error(${db_name}.conn)); }')
g.writeln('MYSQL_RES* $rs = mysql_store_result(${db_name}.conn);')
g.writeln('if (mysql_num_rows($rs) != 1) { puts("Something went wrong"); }')
g.writeln('MYSQL_ROW ${rs}_row = mysql_fetch_row($rs);')
id_name := g.new_tmp_var()
g.writeln('int $id_name = string_int(tos_clone(${rs}_row[0]));')
g.writeln('mysql_free_result($rs);')
g.sql_arr_stmt(arr_stmt, arr_fkeys, id_name, db_expr)
}
}
fn (mut g Gen) mysql_select_expr(node ast.SqlExpr, sub bool, line string, typ SqlType) {
@ -536,7 +627,14 @@ fn (mut g Gen) mysql_select_expr(node ast.SqlExpr, sub bool, line string, typ Sq
char_ptr := g.new_tmp_var()
g.writeln('char* $char_ptr = "";')
mut primary := ''
for i, field in node.fields {
for attr in field.attrs {
if attr.name == 'primary' {
primary = '${tmp}.$field.name'
break
}
}
g.writeln('$char_ptr = $fields[$i];')
g.writeln('if ($char_ptr == NULL) { $char_ptr = ""; }')
name := g.table.get_type_symbol(field.typ).cname
@ -563,6 +661,8 @@ fn (mut g Gen) mysql_select_expr(node ast.SqlExpr, sub bool, line string, typ Sq
g.sql_buf = tmp_sql_buf
g.sql_i = tmp_sql_i
g.sql_table_name = tmp_sql_table_name
} else if g.table.get_type_symbol(field.typ).kind == .array {
g.sql_select_arr(field, node, primary, tmp)
} else if field.typ == ast.string_type {
g.writeln('${tmp}.$field.name = tos_clone($char_ptr);')
} else if field.typ == ast.byte_type {
@ -724,15 +824,23 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
g.sql_defaults(node, typ)
g.writeln(';')
mut arr_stmt := []ast.SqlStmtLine{}
mut arr_fkeys := []string{}
if node.kind == .insert {
for i, field in node.fields {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
g.sql_i = i
field_type := g.get_sql_field_type(field)
if field.name == g.sql_fkey && g.sql_fkey != '' {
g.sql_buf = strings.new_builder(100)
g.sql_bind(g.sql_parent_id, '', field_type, typ)
g.writeln(g.sql_buf.str())
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)]
@ -749,6 +857,24 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
g.sql_bind('string_int((*(string*)array_get((*(pg__Row*)${res}.data).vals, 0)))',
'', ast.int_type, typ)
g.writeln(g.sql_buf.str())
} else if g.table.get_type_symbol(field.typ).kind == .array {
t := g.table.get_type_symbol(field.typ).array_info().elem_type
if g.table.get_type_symbol(t).kind == .struct_ {
mut fkey := ''
for attr in field.attrs {
if attr.name == 'fkey' && attr.arg != '' && attr.kind == .string {
fkey = attr.arg
break
}
}
if fkey == '' {
verror('fkey attribute has to be set for arrays in orm')
continue
}
arr_stmt << node.sub_structs[int(t)]
arr_fkeys << fkey
}
} else {
g.sql_buf = strings.new_builder(100)
g.sql_bind(x, '', field_type, typ)
@ -761,6 +887,16 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) {
g.writeln(binds)
g.writeln('pg__DB_exec($db_name, $g.sql_stmt_name);')
if arr_stmt.len > 0 {
res := g.new_tmp_var()
g.writeln('Option_pg__Row $res = pg__DB_exec_one($db_name, _SLIT("SELECT LASTVAL();"));')
g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln(_STR("\\000%.*s", 2, IError_str(err))); }')
id_name := g.new_tmp_var()
g.writeln('int $id_name = string_int((*(string*)array_get((*(pg__Row*)${res}.data).vals, 0)));')
g.sql_arr_stmt(arr_stmt, arr_fkeys, id_name, db_expr)
}
}
fn (mut g Gen) psql_select_expr(node ast.SqlExpr, sub bool, line string, typ SqlType) {
@ -834,7 +970,18 @@ fn (mut g Gen) psql_select_expr(node ast.SqlExpr, sub bool, line string, typ Sql
g.writeln('Array_string $fields = (*(pg__Row*) array_get($rows, $tmp_i)).vals;')
fld := g.new_tmp_var()
g.writeln('string $fld;')
mut primary := ''
for i, field in node.fields {
for attr in field.attrs {
if attr.name == 'primary' {
primary = '${tmp}.$field.name'
break
}
}
if g.table.get_type_symbol(field.typ).kind == .array {
g.sql_select_arr(field, node, primary, tmp)
continue
}
g.writeln('$fld = (*(string*)array_get($fields, $i));')
name := g.table.get_type_symbol(field.typ).cname
@ -965,6 +1112,87 @@ fn (mut g Gen) psql_bind(val string, typ ast.Type) {
// utils
fn (mut g Gen) sql_select_arr(field ast.StructField, node ast.SqlExpr, primary string, tmp string) {
t := g.table.get_type_symbol(field.typ).array_info().elem_type
if g.table.get_type_symbol(t).kind == .struct_ {
mut fkey := ''
for attr in field.attrs {
if attr.name == 'fkey' && attr.arg != '' && attr.kind == .string {
fkey = attr.arg
break
}
}
if fkey == '' {
verror('fkey attribute has to be set for arrays in orm')
return
}
g.writeln('//parse array start')
e := node.sub_structs[int(t)]
mut where_expr := e.where_expr as ast.InfixExpr
mut lidt := where_expr.left as ast.Ident
mut ridt := where_expr.right as ast.Ident
ridt.name = primary
lidt.name = fkey
where_expr.right = ridt
where_expr.left = lidt
expr := ast.SqlExpr{
typ: field.typ
has_where: e.has_where
db_expr: e.db_expr
is_array: true
pos: e.pos
where_expr: where_expr
table_expr: e.table_expr
fields: e.fields
sub_structs: e.sub_structs
}
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 array 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
}
}
fn (mut g Gen) sql_arr_stmt(arr_stmt []ast.SqlStmtLine, arr_fkeys []string, id_name string, db_expr ast.Expr) {
for i, s in arr_stmt {
cnt := g.new_tmp_var()
g.writeln('for (int $cnt = 0; $cnt < ${s.object_var_name}.len; $cnt++) {')
name := g.table.get_type_symbol(s.table_expr.typ).cname
tmp_var := g.new_tmp_var()
g.writeln('\t$name $tmp_var = (*($name*)array_get($s.object_var_name, $cnt));')
stmt := ast.SqlStmtLine{
pos: s.pos
kind: s.kind
table_expr: s.table_expr
object_var_name: tmp_var
fields: s.fields
sub_structs: s.sub_structs
}
tmp_fkey := g.sql_fkey
tmp_parent_id := g.sql_parent_id
g.sql_fkey = arr_fkeys[i]
g.sql_parent_id = id_name
g.sql_stmt_line(stmt, db_expr)
g.sql_fkey = tmp_fkey
g.sql_parent_id = tmp_parent_id
g.writeln('}')
}
}
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)
@ -1003,9 +1231,10 @@ fn (mut g Gen) get_base_sql_select_query(node ast.SqlExpr, typ SqlType) string {
sql_query += 'COUNT(*) FROM $lit$table_name$lit '
} else {
// `select id, name, country from User`
for i, field in node.fields {
fields := node.fields.filter(g.table.get_type_symbol(it.typ).kind != .array)
for i, field in fields {
sql_query += '$lit${g.get_field_name(field)}$lit'
if i < node.fields.len - 1 {
if i < fields.len - 1 {
sql_query += ', '
}
}
@ -1031,22 +1260,23 @@ fn (mut g Gen) sql_defaults(node ast.SqlStmtLine, typ SqlType) {
g.write('DELETE FROM $lit$table_name$lit ')
}
if node.kind == .insert {
for i, field in node.fields {
fields := node.fields.filter(g.table.get_type_symbol(it.typ).kind != .array)
for i, field in fields {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
g.write('$lit${g.get_field_name(field)}$lit')
if i < node.fields.len - 1 {
if i < fields.len - 1 {
g.write(', ')
}
}
g.write(') VALUES (')
for i, field in node.fields {
for i, field in fields {
if g.get_sql_field_type(field) == ast.Type(-1) {
continue
}
g.inc_sql_i(typ)
if i < node.fields.len - 1 {
if i < fields.len - 1 {
g.write(', ')
}
}
@ -1092,6 +1322,7 @@ fn (mut g Gen) table_gen(node ast.SqlStmtLine, typ SqlType, expr ast.Expr) strin
mut is_unique := false
mut is_skip := false
mut unique_len := 0
mut fkey := ''
for attr in field.attrs {
match attr.name {
'primary' {
@ -1117,6 +1348,14 @@ fn (mut g Gen) table_gen(node ast.SqlStmtLine, typ SqlType, expr ast.Expr) strin
'skip' {
is_skip = true
}
'fkey' {
if attr.arg != '' {
if attr.kind == .string {
fkey = attr.arg
continue
}
}
}
else {}
}
}
@ -1136,6 +1375,30 @@ fn (mut g Gen) table_gen(node ast.SqlStmtLine, typ SqlType, expr ast.Expr) strin
pos: node.table_expr.pos
}
}, expr)
} else if g.table.get_type_symbol(field.typ).kind == .array {
arr_info := g.table.get_type_symbol(field.typ).array_info()
if arr_info.nr_dims > 1 {
verror('array with one dim are supported in orm')
continue
}
atyp := arr_info.elem_type
if g.table.get_type_symbol(atyp).kind == .struct_ {
if fkey == '' {
verror('array field ($field.name) needs a fkey')
continue
}
g.sql_create_table(ast.SqlStmtLine{
kind: node.kind
pos: node.pos
table_expr: ast.TypeNode{
typ: atyp
pos: node.table_expr.pos
}
}, expr)
} else {
verror('unknown type ($field.typ) for field $field.name in struct $table_name')
}
continue
} else {
verror('unknown type ($field.typ) for field $field.name in struct $table_name')
continue