orm: add type detection of db (#8756)
							parent
							
								
									94acc27ee6
								
							
						
					
					
						commit
						4bdbb0cfa8
					
				|  | @ -17,7 +17,62 @@ enum SqlExprSide { | |||
| 	right | ||||
| } | ||||
| 
 | ||||
| enum SqlType { | ||||
| 	sqlite3 | ||||
| 	mysql | ||||
| 	psql | ||||
| 	unknown | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) sql_stmt(node ast.SqlStmt) { | ||||
| 	typ := g.parse_db_type(node.db_expr) | ||||
| 	match typ { | ||||
| 		.sqlite3 { | ||||
| 			g.sqlite3_stmt(node, typ) | ||||
| 		} | ||||
| 		else { | ||||
| 			verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) { | ||||
| 	typ := g.parse_db_type(node.db_expr) | ||||
| 	match typ { | ||||
| 		.sqlite3 { | ||||
| 			g.sqlite3_select_expr(node, sub, line, typ) | ||||
| 		} | ||||
| 		else { | ||||
| 			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) { | ||||
| 	match typ { | ||||
| 		.sqlite3 { | ||||
| 			g.sqlite3_bind_int(val) | ||||
| 		} | ||||
| 		else { | ||||
| 			// add error
 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // sqlite3
 | ||||
| 
 | ||||
| fn (mut g Gen) sqlite3_stmt(node ast.SqlStmt, typ SqlType) { | ||||
| 	g.sql_i = 0 | ||||
| 	g.writeln('\n\t// sql insert') | ||||
| 	db_name := g.new_tmp_var() | ||||
|  | @ -58,7 +113,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) { | |||
| 	} else if node.kind == .update { | ||||
| 		for i, col in node.updated_columns { | ||||
| 			g.write(' $col = ') | ||||
| 			g.expr_to_sql(node.update_exprs[i]) | ||||
| 			g.expr_to_sql(node.update_exprs[i], typ) | ||||
| 			if i < node.updated_columns.len - 1 { | ||||
| 				g.write(', ') | ||||
| 			} | ||||
|  | @ -68,7 +123,7 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) { | |||
| 		g.write(' WHERE ') | ||||
| 	} | ||||
| 	if node.kind == .update || node.kind == .delete { | ||||
| 		g.expr_to_sql(node.where_expr) | ||||
| 		g.expr_to_sql(node.where_expr, typ) | ||||
| 	} | ||||
| 	g.writeln('"));') | ||||
| 	if node.kind == .insert { | ||||
|  | @ -106,14 +161,14 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) { | |||
| 	g.writeln('\tsqlite3_finalize($g.sql_stmt_name);') | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) { | ||||
| fn (mut g Gen) sqlite3_select_expr(node ast.SqlExpr, sub bool, line string, sql_typ SqlType) { | ||||
| 	g.sql_i = 0 | ||||
| 	/* | ||||
| 	`nr_users := sql db { ... }` => | ||||
| 	``` | ||||
| 		sql_init_stmt() | ||||
| 		sql_bind_int() | ||||
| 		sql_bind_string() | ||||
| 		sqlite3_bind_int() | ||||
| 		sqlite3_bind_string() | ||||
| 		... | ||||
| 		int nr_users = get_int(stmt) | ||||
| 	``` | ||||
|  | @ -152,12 +207,12 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) { | |||
| 	g.write('sqlite3_stmt* $g.sql_stmt_name = ${c.dbtype}__DB_init_stmt($db_name, _SLIT("') | ||||
| 	g.write(sql_query) | ||||
| 	if node.has_where && node.where_expr is ast.InfixExpr { | ||||
| 		g.expr_to_sql(node.where_expr) | ||||
| 		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) | ||||
| 		g.expr_to_sql(node.order_expr, sql_typ) | ||||
| 		if node.has_desc { | ||||
| 			g.write(' DESC ') | ||||
| 		} | ||||
|  | @ -167,12 +222,12 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) { | |||
| 	if node.has_limit { | ||||
| 		g.write(' LIMIT ') | ||||
| 		g.sql_side = .right | ||||
| 		g.expr_to_sql(node.limit_expr) | ||||
| 		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) | ||||
| 		g.expr_to_sql(node.offset_expr, sql_typ) | ||||
| 	} | ||||
| 	g.writeln('"));') | ||||
| 	// Dump all sql parameters generated by our custom expr handler
 | ||||
|  | @ -283,15 +338,31 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr, sub bool, line string) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) sql_bind_int(val 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) sql_bind_string(val string, len string) { | ||||
| 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) expr_to_sql(expr ast.Expr) { | ||||
| // 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) { | ||||
| 	// 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()
 | ||||
| 	//
 | ||||
|  | @ -300,7 +371,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { | |||
| 	match expr { | ||||
| 		ast.InfixExpr { | ||||
| 			g.sql_side = .left | ||||
| 			g.expr_to_sql(expr.left) | ||||
| 			g.expr_to_sql(expr.left, typ) | ||||
| 			match expr.op { | ||||
| 				.eq { g.write(' = ') } | ||||
| 				.gt { g.write(' > ') } | ||||
|  | @ -316,22 +387,22 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { | |||
| 				else {} | ||||
| 			} | ||||
| 			g.sql_side = .right | ||||
| 			g.expr_to_sql(expr.right) | ||||
| 			g.expr_to_sql(expr.right, typ) | ||||
| 		} | ||||
| 		ast.StringLiteral { | ||||
| 			// g.write("'$it.val'")
 | ||||
| 			g.inc_sql_i() | ||||
| 			g.sql_bind_string('"$expr.val"', expr.val.len.str()) | ||||
| 			g.sql_bind_string('"$expr.val"', expr.val.len.str(), typ) | ||||
| 		} | ||||
| 		ast.IntegerLiteral { | ||||
| 			g.inc_sql_i() | ||||
| 			g.sql_bind_int(expr.val) | ||||
| 			g.sql_bind_int(expr.val, typ) | ||||
| 		} | ||||
| 		ast.BoolLiteral { | ||||
| 			// 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 )
 | ||||
| 			g.inc_sql_i() | ||||
| 			g.sql_bind_int(if expr.val { '1' } else { '0' }) | ||||
| 			g.sql_bind_int(if expr.val { '1' } else { '0' }, typ) | ||||
| 		} | ||||
| 		ast.Ident { | ||||
| 			// `name == user_name` => `name == ?1`
 | ||||
|  | @ -342,13 +413,13 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { | |||
| 			} else { | ||||
| 				g.inc_sql_i() | ||||
| 				info := expr.info as ast.IdentVar | ||||
| 				typ := info.typ | ||||
| 				if typ == table.string_type { | ||||
| 					g.sql_bind_string('${expr.name}.str', '${expr.name}.len') | ||||
| 				} else if typ == table.int_type { | ||||
| 					g.sql_bind_int(expr.name) | ||||
| 				ityp := info.typ | ||||
| 				if ityp == table.string_type { | ||||
| 					g.sql_bind_string('${expr.name}.str', '${expr.name}.len', typ) | ||||
| 				} else if ityp == table.int_type { | ||||
| 					g.sql_bind_int(expr.name, typ) | ||||
| 				} else { | ||||
| 					verror('bad sql type=$typ ident_name=$expr.name') | ||||
| 					verror('bad sql type=$ityp ident_name=$expr.name') | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -359,7 +430,7 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { | |||
| 					verror('orm selector not ident') | ||||
| 				} | ||||
| 				ident := expr.expr as ast.Ident | ||||
| 				g.sql_bind_int(ident.name + '.' + expr.field_name) | ||||
| 				g.sql_bind_int(ident.name + '.' + expr.field_name, typ) | ||||
| 			} else { | ||||
| 				verror('bad sql type=$expr.typ selector expr=$expr.field_name') | ||||
| 			} | ||||
|  | @ -380,3 +451,31 @@ fn (mut g Gen) inc_sql_i() { | |||
| 	g.sql_i++ | ||||
| 	g.write('?$g.sql_i') | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType { | ||||
| 	match expr { | ||||
| 		ast.Ident { | ||||
| 			if expr.info is ast.IdentVar { | ||||
| 				return g.parse_db_from_type_string(g.table.get_type_name(expr.info.typ)) | ||||
| 			} | ||||
| 		} | ||||
| 		ast.SelectorExpr { | ||||
| 			return g.parse_db_from_type_string(g.table.get_type_name(expr.typ)) | ||||
| 		} | ||||
| 		else { | ||||
| 			return .unknown | ||||
| 		} | ||||
| 	} | ||||
| 	return .unknown | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) parse_db_from_type_string(name string) SqlType { | ||||
| 	match name { | ||||
| 		'sqlite.DB' { | ||||
| 			return .sqlite3 | ||||
| 		} | ||||
| 		else { | ||||
| 			return .unknown | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue