Răsfoiți Sursa

[java] Java SQL initial implementation

Cauê Waneck 11 ani în urmă
părinte
comite
73cd40a48b
2 a modificat fișierele cu 346 adăugiri și 0 ștergeri
  1. 23 0
      std/java/_std/sys/db/Sqlite.hx
  2. 323 0
      std/java/db/Jdbc.hx

+ 23 - 0
std/java/_std/sys/db/Sqlite.hx

@@ -0,0 +1,23 @@
+package sys.db;
+
+class Sqlite
+{
+	static var init = false;
+	/**
+		Opens a new SQLite connection on the specified path.
+		Note that you will need a SQLite JDBC driver (like https://bitbucket.org/xerial/sqlite-jdbc).
+	**/
+	public static function open(file:String):sys.db.Connection
+	{
+		if (!init)
+		{
+			try java.lang.Class.forName("org.sqlite.JDBC") catch(e:Dynamic) throw e;
+			init = true;
+		}
+		try
+		{
+			var cnx = java.sql.DriverManager.getConnection("jdbc:sqlite:" + file);
+			return java.db.Jdbc.create(cnx);
+		} catch(e:Dynamic) throw e;
+	}
+}

+ 323 - 0
std/java/db/Jdbc.hx

@@ -0,0 +1,323 @@
+package java.db;
+import java.util.concurrent.atomic.AtomicInteger;
+import haxe.io.Bytes;
+import java.sql.Types;
+
+@:native('haxe.java.db.Jdbc')
+class Jdbc
+{
+
+	public static function create(cnx:java.sql.Connection):sys.db.Connection
+	{
+		return new JdbcConnection(cnx);
+	}
+
+}
+
+@:native('haxe.java.db.JdbcConnection')
+private class JdbcConnection implements sys.db.Connection
+{
+	private static var ids = new AtomicInteger(0);
+	private var id:Int;
+
+	private var cnx:java.sql.Connection;
+	private var _lastInsertId:Int;
+	//escape handling
+	private var escapeRegex:EReg;
+	private var escapes:Array<Dynamic>;
+
+	public function new(cnx)
+	{
+		this.id = ids.getAndIncrement();
+		this.cnx = cnx;
+		this.escapes = [];
+		this.escapeRegex = ~/@@HX_ESCAPE(\d+)_(\d+)@@/;
+	}
+
+	public function close()
+	{
+		try
+			this.cnx.close()
+		catch(e:Dynamic) throw e;
+	}
+
+	public function escape(s:String):String
+	{
+		return "@@HX_ESCAPE" + id + "_" +escapes.push(s) + "@@";
+	}
+
+	public function quote(s:String):String
+	{
+		return "@@HX_ESCAPE" + id + "_" +escapes.push(s) + "@@";
+	}
+
+	public function addValue(s:StringBuf, v:Dynamic)
+	{
+		if (Std.is(v, Date))
+		{
+			if (dbName() == "SQLite")
+			{
+				v = Std.string(v);
+			} else {
+				var d:Date = v;
+				v = new java.sql.Date(cast(d.getTime(), haxe.Int64));
+			}
+		} else if (Std.is(v, Bytes)) {
+			var bt:Bytes = v;
+			v = bt.getData();
+		}
+		s.add("@@HX_ESCAPE");
+		s.add(id);
+		s.add("_");
+		s.add(escapes.push(v));
+		s.add("@@");
+	}
+
+	public function lastInsertId():Int
+	{
+		return _lastInsertId;
+	}
+
+	public function dbName():String
+	{
+		try {
+			var ret = cnx.getMetaData().getDriverName();
+			var retc = ret.toLowerCase();
+			if (retc.indexOf("mysql") != -1)
+				return "MySQL";
+			else if (retc.indexOf("sqlite") != -1)
+				return "SQLite";
+			return ret;
+		} catch(e:Dynamic) { throw e; }
+	}
+
+	public function startTransaction()
+	{
+		try
+		{
+			cnx.setAutoCommit(false);
+		}
+		catch(e:Dynamic) throw e;
+	}
+
+	public function commit()
+	{
+		try
+		{
+			cnx.commit();
+		}
+		catch(e:Dynamic)
+		{
+			throw e;
+		}
+	}
+
+	public function rollback()
+	{
+		try
+			cnx.rollback()
+		catch(e:Dynamic) throw e;
+	}
+
+	public function request(s:String):sys.db.ResultSet
+	{
+		var newst = new StringBuf();
+		var sentArray = [];
+
+		//cycle through the request string, adding any @@HX_ESCAPE@@ reference to the sentArray
+		var r = escapeRegex;
+		var myid = id + "", escapes = escapes, elen = escapes.length;
+		try
+		{
+			while (r.match(s))
+			{
+				var id = r.matched(1);
+				if (id != myid) throw "Request quotes are only valid for one single request; They can't be cached.";
+
+				newst.add(r.matchedLeft());
+				var eid = Std.parseInt(r.matched(2));
+				if (eid == null || eid > elen)
+					throw "Invalid request quote ID " + eid;
+				sentArray.push(escapes[eid - 1]);
+				newst.add("?");
+				s = r.matchedRight();
+			}
+			newst.add(s);
+			var stmt = cnx.prepareStatement(newst.toString());
+			for (i in 0...sentArray.length)
+			{
+				stmt.setObject(i + 1, sentArray[i]);
+			}
+
+			var ret = null, dbName = dbName();
+			if (stmt.execute())
+			{
+				//is a result set
+				var rs = stmt.getResultSet();
+				ret = new JdbcResultSet(rs, dbName, stmt.getMetaData());
+			} else {
+				//is an update
+				var affected = stmt.getUpdateCount();
+				if (affected == 1)
+				{
+					var autogen = stmt.getGeneratedKeys();
+					if (autogen.next())
+					{
+						this._lastInsertId = autogen.getInt(1);
+					}
+				}
+				ret = new JdbcResultSet(null, dbName,null);
+			}
+
+			if (escapes.length != 0)
+				escapes = [];
+			this.id = ids.getAndIncrement();
+			return ret;
+		}
+		catch(e:Dynamic)
+		{
+			if (escapes.length != 0)
+				escapes = [];
+			this.id = ids.getAndIncrement();
+			throw e;
+		}
+	}
+
+}
+
+@:native('haxe.java.db.JdbcResultSet')
+private class JdbcResultSet implements sys.db.ResultSet
+{
+	@:isVar public var length(get,null) : Int;
+	public var nfields(get,null) : Int;
+
+	private var rs:java.sql.ResultSet;
+	private var names:Array<String>;
+	private var types:java.NativeArray<Int>;
+	private var dbName:String;
+
+	public function new(rs, dbName, meta:java.sql.ResultSetMetaData)
+	{
+		this.dbName = dbName;
+		this.rs = rs;
+		if (meta != null)
+		{
+			try {
+				var count = meta.getColumnCount();
+				var names = [], types = new NativeArray(count);
+				for (i in 0...count)
+				{
+					names.push(meta.getColumnName(i+1));
+					types[i] = meta.getColumnType(i+1);
+				}
+				this.types = types;
+				this.names = names;
+			} catch(e:Dynamic) throw e;
+		}
+	}
+
+	private function get_length():Int
+	{
+		if (length == 0)
+		{
+      try
+      {
+  			var cur = rs.getRow();
+  			rs.last();
+  			this.length = rs.getRow();
+  			rs.absolute(cur);
+      } catch(e:Dynamic) throw e;
+		}
+		return length;
+	}
+
+	private function get_nfields():Int
+	{
+		return names == null ? 0 : names.length;
+	}
+
+	public function hasNext() : Bool
+	{
+		try
+		{
+			return rs != null && rs.next();
+		}
+		catch(e:Dynamic) { return throw e; }
+	}
+
+	public function next() : Dynamic
+	{
+		try {
+			if (rs == null) return null;
+			var ret = {}, names = names, types = types;
+			for (i in 0...names.length)
+			{
+				var name = names[i], t = types[i], val:Dynamic = null;
+				if (t == Types.FLOAT)
+				{
+					val = rs.getDouble(i+1);
+				} else if (t == Types.DATE || t == Types.TIME) {
+					if (dbName == "SQLite")
+					{
+						var d:Date = Date.fromString(rs.getString(i+1));
+						val = d;
+					} else {
+						var d:java.sql.Date = rs.getDate(i+1);
+						val = Date.fromTime(cast d.getTime());
+					}
+				} else if (t == Types.LONGVARBINARY || t == Types.VARBINARY || t == Types.BINARY || t == Types.BLOB) {
+					var b = rs.getBytes(i+1);
+					val = Bytes.ofData(b);
+				} else {
+					untyped __java__("val = rs.getObject(i + 1)"); //type parameter constraint + overloads
+				}
+				Reflect.setField(ret, name, val);
+			}
+			return ret;
+		} catch(e:Dynamic) throw e;
+	}
+
+	public function results() : List<Dynamic>
+	{
+		var l = new List();
+		if (rs == null) return l;
+
+		try
+		{
+			while(rs.next())
+				l.add(next());
+		} catch(e:Dynamic) throw e;
+		return l;
+	}
+
+	public function getResult( n : Int ) : String
+	{
+    try
+    {
+  		return rs.getString(n);
+    } catch(e:Dynamic) throw e;
+    return null;
+	}
+
+	public function getIntResult( n : Int ) : Int
+	{
+		try
+		{
+			return rs.getInt(n);
+		}
+		catch(e:Dynamic) { return throw e; };
+	}
+
+	public function getFloatResult( n : Int ) : Float
+	{
+    try
+    {
+  		return rs.getFloat(n);
+    } catch(e:Dynamic) { return throw e; };
+	}
+
+	public function getFieldsNames() : Null<Array<String>>
+	{
+		return this.names;
+	}
+}