Browse Source

Merge pull request #3291 from waneck/typed_spod

SPOD fixes. Closes #2273 . Closes #2110 . Closes #1880 .
Cauê Waneck 11 years ago
parent
commit
b52001cda6
9 changed files with 355 additions and 21 deletions
  1. 1 0
      .gitignore
  2. 3 0
      .travis.yml
  3. 13 2
      std/php/db/PDO.hx
  4. 33 0
      std/sys/db/Manager.hx
  5. 1 1
      std/sys/db/RecordMacros.hx
  6. 19 18
      tests/RunTravis.hx
  7. 52 0
      tests/unit/MySpodClass.hx
  8. 15 0
      tests/unit/Test.hx
  9. 218 0
      tests/unit/TestSpod.hx

+ 1 - 0
.gitignore

@@ -41,6 +41,7 @@
 /tests/unit/cs
 /tests/unit/cs
 /tests/unit/cs_unsafe
 /tests/unit/cs_unsafe
 /tests/unit/dump
 /tests/unit/dump
+/tests/unit/db.db3
 
 
 /tests/unit/native_java/obj
 /tests/unit/native_java/obj
 /tests/unit/native_java/cmd
 /tests/unit/native_java/cmd

+ 3 - 0
.travis.yml

@@ -29,6 +29,9 @@ matrix:
   allow_failures:
   allow_failures:
     - env: TEST=flash8
     - env: TEST=flash8
 
 
+before_script:
+  - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then mysql -u root -e "CREATE DATABASE haxe_test;"; fi
+
 install:
 install:
   - if [ -z "${TRAVIS_OS_NAME}" ]; then export TRAVIS_OS_NAME=linux; fi; # for our forks that do not have mult-os enabled.
   - if [ -z "${TRAVIS_OS_NAME}" ]; then export TRAVIS_OS_NAME=linux; fi; # for our forks that do not have mult-os enabled.
   - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then travis_retry sudo apt-get update -qq; travis_retry sudo apt-get install ocaml zlib1g-dev libgc-dev -qq; fi
   - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then travis_retry sudo apt-get update -qq; travis_retry sudo apt-get install ocaml zlib1g-dev libgc-dev -qq; fi

+ 13 - 2
std/php/db/PDO.hx

@@ -100,6 +100,13 @@ private class PDOConnection implements Connection {
 			pdo = untyped __call__("new PDO", dsn, user, password, arr);
 			pdo = untyped __call__("new PDO", dsn, user, password, arr);
 		}
 		}
 		dbname = dsn.split(':').shift();
 		dbname = dsn.split(':').shift();
+		switch(dbname.toLowerCase())
+		{
+			case "sqlite":
+				dbname = "SQLite";
+			case "mysql":
+				dbname = "MySQL";
+		}
 	}
 	}
 
 
 	public function close() {
 	public function close() {
@@ -190,6 +197,8 @@ private class TypeStrategy {
 				return untyped __call__("floatval", v);
 				return untyped __call__("floatval", v);
 			case "date":
 			case "date":
 				return Date.fromString(v);
 				return Date.fromString(v);
+			case "blob":
+				return haxe.io.Bytes.ofString(v);
 			default:
 			default:
 				return v;
 				return v;
 		}
 		}
@@ -216,10 +225,12 @@ private class PHPNativeStrategy extends TypeStrategy {
 				return "float";
 				return "float";
 			case "date", "datetime":
 			case "date", "datetime":
 				return "date";
 				return "date";
-			case "bool":
+			case "bool", "tinyint(1)", "tiny":
 				return "bool";
 				return "bool";
 			case "int", "int24", "int32", "long", "longlong", "short":
 			case "int", "int24", "int32", "long", "longlong", "short":
 				return "int";
 				return "int";
+			case "blob":
+				return "blob";
 			default:
 			default:
 				return "string";
 				return "string";
 		}
 		}
@@ -403,4 +414,4 @@ private class PDOResultSet extends BaseResultSet {
 			return v;
 			return v;
 		}
 		}
 	}
 	}
-}
+}

+ 33 - 0
std/sys/db/Manager.hx

@@ -177,6 +177,11 @@ class Manager<T : Object> {
 		if( table_keys.length == 1 && Reflect.field(x,table_keys[0]) == null )
 		if( table_keys.length == 1 && Reflect.field(x,table_keys[0]) == null )
 			Reflect.setField(x,table_keys[0],getCnx().lastInsertId());
 			Reflect.setField(x,table_keys[0],getCnx().lastInsertId());
 		addToCache(x);
 		addToCache(x);
+		var cache = Reflect.field(x,cache_field);
+		if (cache == null)
+		{
+			Reflect.setField(x,cache_field,{});
+		}
 	}
 	}
 
 
 	inline function isBinary( t : RecordInfos.RecordType ) {
 	inline function isBinary( t : RecordInfos.RecordType ) {
@@ -314,6 +319,34 @@ class Manager<T : Object> {
 			Reflect.setField(o, f, Reflect.field(x, f));
 			Reflect.setField(o, f, Reflect.field(x, f));
 		untyped o._manager = this;
 		untyped o._manager = this;
 		#end
 		#end
+		for (f in Reflect.fields(x) )
+		{
+			var val:Dynamic = Reflect.field(x,f), info = table_infos.hfields.get(f);
+			if (val != null && info != null) switch (info.t) {
+				case DDate, DDateTime if (!Std.is(val,Date)):
+					if (Std.is(val,Float))
+					{
+						val = Date.fromTime(val);
+					} else {
+						val = Date.fromString(val +"");
+					}
+				case DSmallBinary, DLongBinary, DBinary, DBytes(_), DData if (Std.is(val, String)):
+					val = haxe.io.Bytes.ofString(val);
+				case DBool if (!Std.is(val,Bool)):
+					if (Std.is(val,Int))
+						val = val != 0;
+					else if (Std.is(val, String)) switch (val.toLowerCase()) {
+						case "1", "true": val = true;
+						case "0", "false": val = false;
+					}
+				case DFloat if (Std.is(val,String)):
+					val = Std.parseFloat(val);
+				case _:
+			}
+
+			Reflect.setField(o, f, val);
+			Reflect.setField(x, f, val);
+		}
 		Reflect.setField(o,cache_field,x);
 		Reflect.setField(o,cache_field,x);
 		addToCache(o);
 		addToCache(o);
 		untyped o._lock = lock;
 		untyped o._lock = lock;

+ 1 - 1
std/sys/db/RecordMacros.hx

@@ -1337,4 +1337,4 @@ class RecordMacros {
 
 
 	#end
 	#end
 
 
-}
+}

+ 19 - 18
tests/RunTravis.hx

@@ -267,6 +267,7 @@ class RunTravis {
 		switch (systemName) {
 		switch (systemName) {
 			case "Linux":
 			case "Linux":
 				runCommand("sudo", ["apt-get", "install", "php5", "-qq"], true);
 				runCommand("sudo", ["apt-get", "install", "php5", "-qq"], true);
+				runCommand("sudo", ["apt-get", "install", "php5-mysql", "php5-sqlite", "-qq"], true);
 			case "Mac":
 			case "Mac":
 				//pass
 				//pass
 		}
 		}
@@ -375,12 +376,12 @@ class RunTravis {
 	static function main():Void {
 	static function main():Void {
 		changeDirectory(unitDir);
 		changeDirectory(unitDir);
 		Sys.putEnv("OCAMLRUNPARAM", "b");
 		Sys.putEnv("OCAMLRUNPARAM", "b");
-		
+
 		var args = ["foo", "12", "a b c\\ &<>[\"]#{}|"];
 		var args = ["foo", "12", "a b c\\ &<>[\"]#{}|"];
-		
+
 		switch (test) {
 		switch (test) {
 			case Macro, null:
 			case Macro, null:
-				runCommand("haxe", ["compile-macro.hxml"]);
+				runCommand("haxe", ["compile-macro.hxml","-D","travis"]);
 
 
 				changeDirectory(miscDir);
 				changeDirectory(miscDir);
 				runCommand("haxe", ["compile.hxml"]);
 				runCommand("haxe", ["compile.hxml"]);
@@ -411,45 +412,45 @@ class RunTravis {
 					//runCommand("haxe", ["compile-macro.hxml"]);
 					//runCommand("haxe", ["compile-macro.hxml"]);
 				//}
 				//}
 			case Neko:
 			case Neko:
-				runCommand("haxe", ["compile-neko.hxml"]);
+				runCommand("haxe", ["compile-neko.hxml","-D","travis"]);
 				runCommand("neko", ["unit.n"]);
 				runCommand("neko", ["unit.n"]);
 
 
 				changeDirectory(sysDir);
 				changeDirectory(sysDir);
-				runCommand("haxe", ["compile-neko.hxml"]);
+				runCommand("haxe", ["compile-neko.hxml","-D","travis"]);
 				changeDirectory("bin/neko");
 				changeDirectory("bin/neko");
 				runCommand("neko", ["sys.n"].concat(args));
 				runCommand("neko", ["sys.n"].concat(args));
 			case Php:
 			case Php:
 				getPhpDependencies();
 				getPhpDependencies();
-				runCommand("haxe", ["compile-php.hxml"]);
+				runCommand("haxe", ["compile-php.hxml","-D","travis"]);
 				runCommand("php", ["php/index.php"]);
 				runCommand("php", ["php/index.php"]);
 			case Python:
 			case Python:
 				getPythonDependencies();
 				getPythonDependencies();
-				runCommand("haxe", ["compile-python.hxml"]);
+				runCommand("haxe", ["compile-python.hxml","-D","travis"]);
 				runCommand("python3", ["unit.py"]);
 				runCommand("python3", ["unit.py"]);
 
 
 				changeDirectory(sysDir);
 				changeDirectory(sysDir);
-				runCommand("haxe", ["compile-python.hxml"]);
+				runCommand("haxe", ["compile-python.hxml","-D","travis"]);
 				changeDirectory("bin/python");
 				changeDirectory("bin/python");
 				runCommand("python3", ["sys.py"].concat(args));
 				runCommand("python3", ["sys.py"].concat(args));
 			case Cpp:
 			case Cpp:
 				getCppDependencies();
 				getCppDependencies();
-				runCommand("haxe", ["compile-cpp.hxml"]);
+				runCommand("haxe", ["compile-cpp.hxml","-D","travis"]);
 				runCommand("./cpp/Test-debug", []);
 				runCommand("./cpp/Test-debug", []);
 
 
 				runCommand("rm", ["-rf", "cpp"]);
 				runCommand("rm", ["-rf", "cpp"]);
 
 
-				runCommand("haxe", ["compile-cpp.hxml", "-D", "HXCPP_M64"]);
+				runCommand("haxe", ["compile-cpp.hxml", "-D", "HXCPP_M64","-D","travis"]);
 				runCommand("./cpp/Test-debug", []);
 				runCommand("./cpp/Test-debug", []);
 
 
 				changeDirectory(sysDir);
 				changeDirectory(sysDir);
-				runCommand("haxe", ["compile-cpp.hxml"]);
+				runCommand("haxe", ["compile-cpp.hxml","-D","travis"]);
 				changeDirectory("bin/cpp");
 				changeDirectory("bin/cpp");
 				runCommand("./Main-debug", args);
 				runCommand("./Main-debug", args);
 			case Js:
 			case Js:
 				getJSDependencies();
 				getJSDependencies();
 
 
 				for (flatten in [true, false]) {
 				for (flatten in [true, false]) {
-					runCommand("haxe", ["compile-js.hxml"].concat(flatten ? ["-D", "js-flatten"] : []));
+					runCommand("haxe", ["compile-js.hxml","-D","travis"].concat(flatten ? ["-D", "js-flatten"] : []));
 					runCommand("node", ["-e", "var unit = require('./unit.js').unit; unit.Test.main(); process.exit(unit.Test.success ? 0 : 1);"]);
 					runCommand("node", ["-e", "var unit = require('./unit.js').unit; unit.Test.main(); process.exit(unit.Test.success ? 0 : 1);"]);
 				}
 				}
 
 
@@ -471,23 +472,23 @@ class RunTravis {
 				runCommand("haxe", ["run.hxml"]);
 				runCommand("haxe", ["run.hxml"]);
 			case Java:
 			case Java:
 				getJavaDependencies();
 				getJavaDependencies();
-				runCommand("haxe", ["compile-java.hxml"]);
+				runCommand("haxe", ["compile-java.hxml","-D","travis"]);
 				runCommand("java", ["-jar", "java/Test-Debug.jar"]);
 				runCommand("java", ["-jar", "java/Test-Debug.jar"]);
 			case Cs:
 			case Cs:
 				getCsDependencies();
 				getCsDependencies();
 
 
-				runCommand("haxe", ["compile-cs.hxml"]);
+				runCommand("haxe", ["compile-cs.hxml","-D","travis"]);
 				runCommand("mono", ["cs/bin/Test-Debug.exe"]);
 				runCommand("mono", ["cs/bin/Test-Debug.exe"]);
 
 
-				runCommand("haxe", ["compile-cs-unsafe.hxml"]);
+				runCommand("haxe", ["compile-cs-unsafe.hxml","-D","travis"]);
 				runCommand("mono", ["cs_unsafe/bin/Test-Debug.exe"]);
 				runCommand("mono", ["cs_unsafe/bin/Test-Debug.exe"]);
 			case Flash9:
 			case Flash9:
 				setupFlashPlayerDebugger();
 				setupFlashPlayerDebugger();
-				runCommand("haxe", ["compile-flash9.hxml", "-D", "fdb"]);
+				runCommand("haxe", ["compile-flash9.hxml", "-D", "fdb","-D","travis"]);
 				runFlash("unit9.swf");
 				runFlash("unit9.swf");
 			case Flash8:
 			case Flash8:
 				setupFlashPlayerDebugger();
 				setupFlashPlayerDebugger();
-				runCommand("haxe", ["compile-flash8.hxml", "-D", "fdb"]);
+				runCommand("haxe", ["compile-flash8.hxml", "-D", "fdb","-D","travis"]);
 				runFlash("unit8.swf");
 				runFlash("unit8.swf");
 			case As3:
 			case As3:
 				setupFlashPlayerDebugger();
 				setupFlashPlayerDebugger();
@@ -504,7 +505,7 @@ class RunTravis {
 				File.saveContent(flexsdkPath + "/env.properties", 'env.PLAYERGLOBAL_HOME=$playerglobalswcFolder');
 				File.saveContent(flexsdkPath + "/env.properties", 'env.PLAYERGLOBAL_HOME=$playerglobalswcFolder');
 				runCommand("mxmlc", ["--version"]);
 				runCommand("mxmlc", ["--version"]);
 
 
-				runCommand("haxe", ["compile-as3.hxml", "-D", "fdb"]);
+				runCommand("haxe", ["compile-as3.hxml", "-D", "fdb","-D","travis"]);
 				runFlash("unit9_as3.swf");
 				runFlash("unit9_as3.swf");
 			case ThirdParty:
 			case ThirdParty:
 				getPhpDependencies();
 				getPhpDependencies();

+ 52 - 0
tests/unit/MySpodClass.hx

@@ -0,0 +1,52 @@
+package unit;
+import sys.db.Object;
+import sys.db.Types;
+
+class MySpodClass extends Object
+{
+  public var theId:SId;
+  public var int:SInt;
+  public var double:SFloat;
+  public var boolean:SBool;
+  public var string:SString<255>;
+  public var date:SDateTime;
+  public var binary:SBinary;
+
+  public var nullInt:SNull<Int>;
+  public var enumFlags:SFlags<SpodEnum>;
+
+  @:relation(rid) public var relation:OtherSpodClass;
+  @:relation(rnid) public var relationNullable:Null<OtherSpodClass>;
+
+  public var data:SData<Array<ComplexClass>>;
+  // public var anEnum:SEnum<SpodEnum>;
+}
+
+class ComplexClass
+{
+	public var val : { name:String, array:Array<String> };
+
+	public function new(val)
+	{
+		this.val = val;
+	}
+}
+
+class OtherSpodClass extends Object
+{
+	public var theid:SId;
+	public var name:SString<255>;
+
+	public function new(name:String)
+	{
+		super();
+		this.name =name;
+	}
+}
+
+enum SpodEnum
+{
+	FirstValue;
+	SecondValue;
+	ThirdValue;
+}

+ 15 - 0
tests/unit/Test.hx

@@ -299,6 +299,21 @@ class Test #if swf_mark implements mt.Protect #end {
 			//new TestUnspecified(),
 			//new TestUnspecified(),
 			//new TestRemoting(),
 			//new TestRemoting(),
 		];
 		];
+		// SPOD tests
+		#if ( (neko || php) && !macro && !interp)
+		#if travis
+		if (Sys.systemName() != "Mac")
+		{
+			classes.push(new TestSpod(sys.db.Mysql.connect({
+				host : "localhost",
+				user : "travis",
+				pass : "",
+				port : 3306,
+				database : "haxe_test" })));
+		}
+		#end
+		classes.push(new TestSpod(sys.db.Sqlite.open("db.db3")));
+		#end
 		TestIssues.addIssueClasses();
 		TestIssues.addIssueClasses();
 		var current = null;
 		var current = null;
 		#if (!fail_eager)
 		#if (!fail_eager)

+ 218 - 0
tests/unit/TestSpod.hx

@@ -0,0 +1,218 @@
+package unit;
+import sys.db.*;
+import haxe.io.Bytes;
+import haxe.EnumFlags;
+import sys.db.Connection;
+import sys.db.Manager;
+import sys.db.Sqlite;
+import sys.db.TableCreate;
+import sys.FileSystem;
+import unit.MySpodClass;
+
+class TestSpod extends Test
+{
+	private var cnx:Connection;
+	public function new(cnx:Connection)
+	{
+		super();
+		this.cnx = cnx;
+		Manager.cnx = cnx;
+		try cnx.request('DROP TABLE MySpodClass') catch(e:Dynamic) {}
+		try cnx.request('DROP TABLE OtherSpodClass') catch(e:Dynamic) {}
+		TableCreate.create(MySpodClass.manager);
+		TableCreate.create(OtherSpodClass.manager);
+	}
+
+	private function setManager()
+	{
+		Manager.initialize();
+		Manager.cnx = cnx;
+		Manager.cleanup();
+	}
+
+	function getDefaultClass()
+	{
+		var scls = new MySpodClass();
+		scls.int = 1;
+		scls.double = 2.0;
+		scls.boolean = true;
+		scls.string = "some string";
+		scls.date = new Date(2012, 07, 30, 0, 0, 0);
+
+		var bytes = Bytes.ofString("\x01\n\r\x02");
+		scls.binary = bytes;
+		scls.enumFlags = EnumFlags.ofInt(0);
+		scls.enumFlags.set(FirstValue);
+		scls.enumFlags.set(ThirdValue);
+
+		scls.data = [new ComplexClass( { name:"test", array:["this", "is", "a", "test"] } )];
+		// scls.anEnum = SecondValue;
+
+		return scls;
+	}
+
+	public function testSpodTypes()
+	{
+		setManager();
+		var c1 = new OtherSpodClass("first spod");
+		c1.insert();
+		var c2 = new OtherSpodClass("second spod");
+		c2.insert();
+
+		var scls = getDefaultClass();
+
+		scls.relation = c1;
+		scls.relationNullable = c2;
+		scls.insert();
+
+		//after inserting, id must be filled
+		t(scls.theId != 0 && scls.theId != null,pos());
+		var theid = scls.theId;
+
+		c1 = c2 = null;
+		Manager.cleanup();
+
+		var cls1 = MySpodClass.manager.get(theid);
+		t(cls1 != null,pos());
+		//after Manager.cleanup(), the instances should be different
+		f(cls1 == scls,pos());
+		scls = null;
+
+		t(Std.is(cls1.int, Int),pos());
+		eq(cls1.int, 1,pos());
+		t(Std.is(cls1.double, Float),pos());
+		eq(cls1.double, 2.0,pos());
+		t(Std.is(cls1.boolean, Bool),pos());
+		eq(cls1.boolean, true,pos());
+		t(Std.is(cls1.string, String),pos());
+		eq(cls1.string, "some string",pos());
+		t(cls1.date != null,pos());
+		t(Std.is(cls1.date, Date),pos());
+		eq(cls1.date.getTime(), new Date(2012, 07, 30, 0, 0, 0).getTime(),pos());
+
+		t(Std.is(cls1.binary, Bytes),pos());
+		eq(cls1.binary.compare(Bytes.ofString("\x01\n\r\x02")), 0,pos());
+		t(cls1.enumFlags.has(FirstValue),pos());
+		f(cls1.enumFlags.has(SecondValue),pos());
+		t(cls1.enumFlags.has(ThirdValue),pos());
+
+		t(Std.is(cls1.data, Array),pos());
+		t(Std.is(cls1.data[0], ComplexClass),pos());
+
+		eq(cls1.data[0].val.name, "test",pos());
+		eq(cls1.data[0].val.array.length, 4,pos());
+		eq(cls1.data[0].val.array[1], "is",pos());
+
+		eq(cls1.relation.name, "first spod",pos());
+		eq(cls1.relationNullable.name, "second spod",pos());
+
+		//test create a new class
+		var scls = getDefaultClass();
+
+		c1 = new OtherSpodClass("third spod");
+		c1.insert();
+
+		scls.relation = c1;
+		scls.insert();
+
+		scls = cls1 = null;
+		Manager.cleanup();
+
+		eq(2, MySpodClass.manager.all().length,pos());
+		var req = MySpodClass.manager.search({ relation: OtherSpodClass.manager.select({ name:"third spod"} ) });
+		eq(req.length, 1,pos());
+		scls = req.first();
+
+		scls.relation.name = "Test";
+		scls.relation.update();
+
+		eq(OtherSpodClass.manager.select({ name:"third spod" }), null,pos());
+
+		for (c in MySpodClass.manager.all())
+			c.delete();
+		for (c in OtherSpodClass.manager.all())
+			c.delete();
+	}
+
+	public function testDateQuery()
+	{
+		setManager();
+		var other1 = new OtherSpodClass("required field");
+		other1.insert();
+
+		var now = Date.now();
+		var c1 = getDefaultClass();
+		c1.relation = other1;
+		c1.date = now;
+		c1.insert();
+
+		var c2 = getDefaultClass();
+		c2.relation = other1;
+		c2.date = DateTools.delta(now, DateTools.hours(1));
+		c2.insert();
+
+		var q = MySpodClass.manager.search($date > now);
+		eq(q.length, 1,pos());
+		eq(q.first(), c2,pos());
+
+		q = MySpodClass.manager.search($date == now);
+		eq(q.length, 1,pos());
+		eq(q.first(), c1,pos());
+
+		q = MySpodClass.manager.search($date >= now);
+		eq(q.length, 2,pos());
+		eq(q.first(), c1,pos());
+
+		q = MySpodClass.manager.search($date >= DateTools.delta(now, DateTools.hours(2)));
+		eq(q.length, 0,pos());
+		eq(q.first(), null,pos());
+
+		c1.delete();
+		c2.delete();
+		other1.delete();
+	}
+
+	public function testData()
+	{
+		setManager();
+		var other1 = new OtherSpodClass("required field");
+		other1.insert();
+
+		var c1 = getDefaultClass();
+		c1.relation = other1;
+		c1.insert();
+
+		eq(c1.data.length,1,pos());
+		c1.data.pop();
+		c1.update();
+
+		Manager.cleanup();
+		c1 = null;
+
+		c1 = MySpodClass.manager.select($relation == other1);
+		eq(c1.data.length, 0,pos());
+		c1.data.push(new ComplexClass({ name: "test1", array:["complex","field"] }));
+		c1.data.push(null);
+		eq(c1.data.length, 2,pos());
+		c1.update();
+
+		Manager.cleanup();
+		c1 = null;
+
+		c1 = MySpodClass.manager.select($relation == other1);
+		eq(c1.data.length,2,pos());
+		eq(c1.data[0].val.name, "test1",pos());
+		eq(c1.data[0].val.array.length, 2,pos());
+		eq(c1.data[0].val.array[0], "complex",pos());
+		eq(c1.data[1], null,pos());
+
+		c1.delete();
+		other1.delete();
+	}
+
+	private function pos(?p:haxe.PosInfos):haxe.PosInfos
+	{
+		p.fileName = p.fileName + "(" + cnx.dbName()  +")";
+		return p;
+	}
+}