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;
|
|
}
|
|
}
|