From 328cb7ed7ed6679fdc01ccd6505d168be8166013 Mon Sep 17 00:00:00 2001
From: Alexander Medvednikov <alexander@vlang.io>
Date: Thu, 25 Jun 2020 22:23:19 +0200
Subject: [PATCH] orm: `update` with selectors and other fixes

---
 0.3_roadmap.txt          |  1 +
 vlib/orm/orm_test.v      | 39 +++++++++++++++++++++++++++++++++++----
 vlib/v/ast/ast.v         |  3 ++-
 vlib/v/checker/checker.v |  7 +++++++
 vlib/v/gen/sql.v         | 15 ++++++++++++++-
 5 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/0.3_roadmap.txt b/0.3_roadmap.txt
index 39e8b44406..cc8c76d28b 100644
--- a/0.3_roadmap.txt
+++ b/0.3_roadmap.txt
@@ -7,6 +7,7 @@
 - C2V translator
 - doom.v
 - rune type, replace ustring with []rune
+- maps with non-string keys
 
 
 
diff --git a/vlib/orm/orm_test.v b/vlib/orm/orm_test.v
index 0d56d0a57e..4b0e796de1 100644
--- a/vlib/orm/orm_test.v
+++ b/vlib/orm/orm_test.v
@@ -106,13 +106,11 @@ fn test_orm_sqlite() {
 	kate := sql db {
 		select from User where id == 3
 	}
-	println(kate)
 	assert kate.is_customer == true
 	//
 	customer := sql db {
 		select from User where is_customer == true limit 1
 	}
-	println(customer)
 	assert customer.is_customer == true
 	assert customer.name == 'Kate'
 	//
@@ -122,19 +120,48 @@ fn test_orm_sqlite() {
 	kate2 := sql db {
 		select from User where id == 3
 	}
-	println(kate2)
 	assert kate2.age == 31
 	assert kate2.name == 'Kate'
 	//
 	sql db {
 		update User set age = 32, name = 'Kate N' where name == 'Kate'
 	}
-	kate3 := sql db {
+	mut kate3 := sql db {
+		select from User where id == 3
+	}
+	assert kate3.age == 32
+	assert kate3.name == 'Kate N'
+	//
+	/*
+	sql db {
+		update User set age = age + 1, name = 'Kate N' where name == 'Kate'
+	}
+	kate3 = sql db {
 		select from User where id == 3
 	}
 	println(kate3)
 	assert kate3.age == 32
 	assert kate3.name == 'Kate N'
+	*/
+	new_age := 33
+	sql db {
+		update User set age = new_age, name = 'Kate N' where id == 3
+	}
+	kate3 = sql db {
+		select from User where id == 3
+	}
+	assert kate3.age == 33
+	assert kate3.name == 'Kate N'
+	//
+	foo := Foo{34}
+	sql db {
+		update User set age = foo.age, name = 'Kate N' where id == 3
+	}
+	kate3 = sql db {
+		select from User where id == 3
+	}
+	assert kate3.age == 34
+	assert kate3.name == 'Kate N'
 }
 
 struct User {
@@ -145,6 +172,10 @@ struct User {
 	skipped_string string [skip]
 }
 
+struct Foo {
+	age int
+}
+
 
 
 fn test_orm_pg() {
diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v
index 6d7c8b8b9c..89b1f5a7a9 100644
--- a/vlib/v/ast/ast.v
+++ b/vlib/v/ast/ast.v
@@ -105,7 +105,8 @@ pub:
 	expr       Expr
 	field_name string
 pub mut:
-	expr_type  table.Type
+	expr_type  table.Type // type of `Foo` in `Foo.bar`
+	typ  table.Type // type of the entire thing (`Foo.bar`)
 }
 
 // module declaration
diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v
index bd2f9745a2..555d262385 100644
--- a/vlib/v/checker/checker.v
+++ b/vlib/v/checker/checker.v
@@ -1252,6 +1252,7 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T
 	// variadic
 	if typ.has_flag(.variadic) {
 		if field_name == 'len' {
+			selector_expr.typ = table.int_type
 			return table.int_type
 		}
 	}
@@ -1259,6 +1260,7 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T
 		if sym.mod != c.mod && !field.is_pub {
 			c.error('field `${sym.name}.$field_name` is not public', selector_expr.pos)
 		}
+		selector_expr.typ = field.typ
 		return field.typ
 	}
 	if sym.kind != .struct_ {
@@ -2721,6 +2723,11 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) table.Type {
 	fields := c.fetch_and_verify_orm_fields(info, node.pos, node.table_name)
 	node.fields = fields
 	c.expr(node.db_expr)
+	if node.kind== .update {
+		for expr in node.update_exprs {
+			c.expr(expr)
+		}
+	}
 	return table.void_type
 }
 
diff --git a/vlib/v/gen/sql.v b/vlib/v/gen/sql.v
index 2132ec5f3d..164e4d589a 100644
--- a/vlib/v/gen/sql.v
+++ b/vlib/v/gen/sql.v
@@ -273,10 +273,23 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) {
 				} else if typ == table.int_type {
 					g.sql_bind_int(expr.name)
 				} else {
-					verror('bad sql type $typ')
+					verror('bad sql type=$typ ident_name=$expr.name')
 				}
 			}
 		}
+		ast.SelectorExpr {
+			g.inc_sql_i()
+			if expr.typ == table.int_type {
+				if expr.expr !is ast.Ident {
+					verror('orm selector not ident')
+				}
+				ident := expr.expr as ast.Ident
+				g.sql_bind_int(ident.name + '.' + expr.field_name)
+			}
+			else {
+				verror('bad sql type=$expr.typ selector expr=$expr.field_name')
+			}
+		}
 		else {
 			g.expr(expr)
 		}