319 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C)2005-2019 Haxe Foundation
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
						|
 * copy of this software and associated documentation files (the "Software"),
 | 
						|
 * to deal in the Software without restriction, including without limitation
 | 
						|
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
						|
 * and/or sell copies of the Software, and to permit persons to whom the
 | 
						|
 * Software is furnished to do so, subject to the following conditions:
 | 
						|
 *
 | 
						|
 * The above copyright notice and this permission notice shall be included in
 | 
						|
 * all copies or substantial portions of the Software.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
						|
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
						|
 * DEALINGS IN THE SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
package java.db;
 | 
						|
 | 
						|
import haxe.io.Bytes;
 | 
						|
import java.sql.Types;
 | 
						|
import java.util.concurrent.atomic.AtomicInteger;
 | 
						|
 | 
						|
@: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.isOfType(v, Date)) {
 | 
						|
			v = Std.string(v);
 | 
						|
		} else if (Std.isOfType(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(),
 | 
						|
				#if jvm java.sql.Statement.RETURN_GENERATED_KEYS #else java.sql.Statement.Statement_Statics.RETURN_GENERATED_KEYS #end);
 | 
						|
			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;
 | 
						|
	private var didNext:Bool;
 | 
						|
 | 
						|
	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 {
 | 
						|
			didNext = true;
 | 
						|
			return rs != null && rs.next();
 | 
						|
		} catch (e:Dynamic) {
 | 
						|
			return throw e;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	public function next():Dynamic {
 | 
						|
		try {
 | 
						|
			if (rs == null)
 | 
						|
				return null;
 | 
						|
			if (didNext) {
 | 
						|
				didNext = false;
 | 
						|
			} else {
 | 
						|
				if (!rs.next()) {
 | 
						|
					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 str = rs.getString(i + 1);
 | 
						|
						if (str != null) {
 | 
						|
							var d:Date = Date.fromString(str);
 | 
						|
							val = d;
 | 
						|
						}
 | 
						|
					} else {
 | 
						|
						var d:java.sql.Date = rs.getDate(i + 1);
 | 
						|
						if (d != null)
 | 
						|
							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);
 | 
						|
					if (b != null)
 | 
						|
						val = Bytes.ofData(b);
 | 
						|
				} else {
 | 
						|
					val = rs.getObject(i + 1);
 | 
						|
				}
 | 
						|
				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 (hasNext())
 | 
						|
				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;
 | 
						|
	}
 | 
						|
 | 
						|
	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;
 | 
						|
	}
 | 
						|
}
 |