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