Jdbc.hx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /*
  2. * Copyright (C)2005-2019 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package java.db;
  23. import haxe.io.Bytes;
  24. import java.sql.Types;
  25. import java.util.concurrent.atomic.AtomicInteger;
  26. @:native('haxe.java.db.Jdbc')
  27. class Jdbc {
  28. public static function create(cnx:java.sql.Connection):sys.db.Connection {
  29. return new JdbcConnection(cnx);
  30. }
  31. }
  32. @:native('haxe.java.db.JdbcConnection')
  33. private class JdbcConnection implements sys.db.Connection {
  34. private static var ids = new AtomicInteger(0);
  35. private var id:Int;
  36. private var cnx:java.sql.Connection;
  37. private var _lastInsertId:Int;
  38. // escape handling
  39. private var escapeRegex:EReg;
  40. private var escapes:Array<Dynamic>;
  41. public function new(cnx) {
  42. this.id = ids.getAndIncrement();
  43. this.cnx = cnx;
  44. this.escapes = [];
  45. this.escapeRegex = ~/@@HX_ESCAPE(\d+)_(\d+)@@/;
  46. }
  47. public function close() {
  48. try
  49. this.cnx.close()
  50. catch (e:Dynamic)
  51. throw e;
  52. }
  53. public function escape(s:String):String {
  54. return "@@HX_ESCAPE" + id + "_" + escapes.push(s) + "@@";
  55. }
  56. public function quote(s:String):String {
  57. return "@@HX_ESCAPE" + id + "_" + escapes.push(s) + "@@";
  58. }
  59. public function addValue(s:StringBuf, v:Dynamic) {
  60. if (Std.isOfType(v, Date)) {
  61. v = Std.string(v);
  62. } else if (Std.isOfType(v, Bytes)) {
  63. var bt:Bytes = v;
  64. v = bt.getData();
  65. }
  66. s.add("@@HX_ESCAPE");
  67. s.add(id);
  68. s.add("_");
  69. s.add(escapes.push(v));
  70. s.add("@@");
  71. }
  72. public function lastInsertId():Int {
  73. return _lastInsertId;
  74. }
  75. public function dbName():String {
  76. try {
  77. var ret = cnx.getMetaData().getDriverName();
  78. var retc = ret.toLowerCase();
  79. if (retc.indexOf("mysql") != -1)
  80. return "MySQL";
  81. else if (retc.indexOf("sqlite") != -1)
  82. return "SQLite";
  83. return ret;
  84. } catch (e:Dynamic) {
  85. throw e;
  86. }
  87. }
  88. public function startTransaction() {
  89. try {
  90. cnx.setAutoCommit(false);
  91. } catch (e:Dynamic)
  92. throw e;
  93. }
  94. public function commit() {
  95. try {
  96. cnx.commit();
  97. } catch (e:Dynamic) {
  98. throw e;
  99. }
  100. }
  101. public function rollback() {
  102. try
  103. cnx.rollback()
  104. catch (e:Dynamic)
  105. throw e;
  106. }
  107. public function request(s:String):sys.db.ResultSet {
  108. var newst = new StringBuf();
  109. var sentArray = [];
  110. // cycle through the request string, adding any @@HX_ESCAPE@@ reference to the sentArray
  111. var r = escapeRegex;
  112. var myid = id + "", escapes = escapes, elen = escapes.length;
  113. try {
  114. while (r.match(s)) {
  115. var id = r.matched(1);
  116. if (id != myid)
  117. throw "Request quotes are only valid for one single request; They can't be cached.";
  118. newst.add(r.matchedLeft());
  119. var eid = Std.parseInt(r.matched(2));
  120. if (eid == null || eid > elen)
  121. throw "Invalid request quote ID " + eid;
  122. sentArray.push(escapes[eid - 1]);
  123. newst.add("?");
  124. s = r.matchedRight();
  125. }
  126. newst.add(s);
  127. var stmt = cnx.prepareStatement(newst.toString(),
  128. #if jvm java.sql.Statement.RETURN_GENERATED_KEYS #else java.sql.Statement.Statement_Statics.RETURN_GENERATED_KEYS #end);
  129. for (i in 0...sentArray.length) {
  130. stmt.setObject(i + 1, sentArray[i]);
  131. }
  132. var ret = null, dbName = dbName();
  133. if (stmt.execute()) {
  134. // is a result set
  135. var rs = stmt.getResultSet();
  136. ret = new JdbcResultSet(rs, dbName, stmt.getMetaData());
  137. } else {
  138. // is an update
  139. var affected = stmt.getUpdateCount();
  140. if (affected == 1) {
  141. var autogen = stmt.getGeneratedKeys();
  142. if (autogen.next()) {
  143. this._lastInsertId = autogen.getInt(1);
  144. }
  145. }
  146. ret = new JdbcResultSet(null, dbName, null);
  147. }
  148. if (escapes.length != 0)
  149. escapes = [];
  150. this.id = ids.getAndIncrement();
  151. return ret;
  152. } catch (e:Dynamic) {
  153. if (escapes.length != 0)
  154. escapes = [];
  155. this.id = ids.getAndIncrement();
  156. throw e;
  157. }
  158. }
  159. }
  160. @:native('haxe.java.db.JdbcResultSet')
  161. private class JdbcResultSet implements sys.db.ResultSet {
  162. @:isVar public var length(get, null):Int;
  163. public var nfields(get, null):Int;
  164. private var rs:java.sql.ResultSet;
  165. private var names:Array<String>;
  166. private var types:java.NativeArray<Int>;
  167. private var dbName:String;
  168. private var didNext:Bool;
  169. public function new(rs, dbName, meta:java.sql.ResultSetMetaData) {
  170. this.dbName = dbName;
  171. this.rs = rs;
  172. if (meta != null) {
  173. try {
  174. var count = meta.getColumnCount();
  175. var names = [], types = new NativeArray(count);
  176. for (i in 0...count) {
  177. names.push(meta.getColumnName(i + 1));
  178. types[i] = meta.getColumnType(i + 1);
  179. }
  180. this.types = types;
  181. this.names = names;
  182. } catch (e:Dynamic)
  183. throw e;
  184. }
  185. }
  186. private function get_length():Int {
  187. if (length == 0) {
  188. try {
  189. var cur = rs.getRow();
  190. rs.last();
  191. this.length = rs.getRow();
  192. rs.absolute(cur);
  193. } catch (e:Dynamic)
  194. throw e;
  195. }
  196. return length;
  197. }
  198. private function get_nfields():Int {
  199. return names == null ? 0 : names.length;
  200. }
  201. public function hasNext():Bool {
  202. try {
  203. didNext = true;
  204. return rs != null && rs.next();
  205. } catch (e:Dynamic) {
  206. return throw e;
  207. }
  208. }
  209. public function next():Dynamic {
  210. try {
  211. if (rs == null)
  212. return null;
  213. if (didNext) {
  214. didNext = false;
  215. } else {
  216. if (!rs.next()) {
  217. return null;
  218. }
  219. }
  220. var ret = {}, names = names, types = types;
  221. for (i in 0...names.length) {
  222. var name = names[i], t = types[i], val:Dynamic = null;
  223. if (t == Types.FLOAT) {
  224. val = rs.getDouble(i + 1);
  225. } else if (t == Types.DATE || t == Types.TIME) {
  226. if (dbName == "SQLite") {
  227. var str = rs.getString(i + 1);
  228. if (str != null) {
  229. var d:Date = Date.fromString(str);
  230. val = d;
  231. }
  232. } else {
  233. var d:java.sql.Date = rs.getDate(i + 1);
  234. if (d != null)
  235. val = Date.fromTime(cast d.getTime());
  236. }
  237. } else if (t == Types.LONGVARBINARY || t == Types.VARBINARY || t == Types.BINARY || t == Types.BLOB) {
  238. var b = rs.getBytes(i + 1);
  239. if (b != null)
  240. val = Bytes.ofData(b);
  241. } else {
  242. val = rs.getObject(i + 1);
  243. }
  244. Reflect.setField(ret, name, val);
  245. }
  246. return ret;
  247. } catch (e:Dynamic)
  248. throw e;
  249. }
  250. public function results():List<Dynamic> {
  251. var l = new List();
  252. if (rs == null)
  253. return l;
  254. try {
  255. while (hasNext())
  256. l.add(next());
  257. } catch (e:Dynamic)
  258. throw e;
  259. return l;
  260. }
  261. public function getResult(n:Int):String {
  262. try {
  263. return rs.getString(n);
  264. } catch (e:Dynamic)
  265. throw e;
  266. }
  267. public function getIntResult(n:Int):Int {
  268. try {
  269. return rs.getInt(n);
  270. } catch (e:Dynamic) {
  271. return throw e;
  272. };
  273. }
  274. public function getFloatResult(n:Int):Float {
  275. try {
  276. return rs.getFloat(n);
  277. } catch (e:Dynamic) {
  278. return throw e;
  279. };
  280. }
  281. public function getFieldsNames():Null<Array<String>> {
  282. return this.names;
  283. }
  284. }