Jdbc.hx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*
  2. * Copyright (C)2005-2017 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 java.util.concurrent.atomic.AtomicInteger;
  24. import haxe.io.Bytes;
  25. import java.sql.Types;
  26. @:native('haxe.java.db.Jdbc')
  27. class Jdbc
  28. {
  29. public static function create(cnx:java.sql.Connection):sys.db.Connection
  30. {
  31. return new JdbcConnection(cnx);
  32. }
  33. }
  34. @:native('haxe.java.db.JdbcConnection')
  35. private class JdbcConnection implements sys.db.Connection
  36. {
  37. private static var ids = new AtomicInteger(0);
  38. private var id:Int;
  39. private var cnx:java.sql.Connection;
  40. private var _lastInsertId:Int;
  41. //escape handling
  42. private var escapeRegex:EReg;
  43. private var escapes:Array<Dynamic>;
  44. public function new(cnx)
  45. {
  46. this.id = ids.getAndIncrement();
  47. this.cnx = cnx;
  48. this.escapes = [];
  49. this.escapeRegex = ~/@@HX_ESCAPE(\d+)_(\d+)@@/;
  50. }
  51. public function close()
  52. {
  53. try
  54. this.cnx.close()
  55. catch(e:Dynamic) throw e;
  56. }
  57. public function escape(s:String):String
  58. {
  59. return "@@HX_ESCAPE" + id + "_" +escapes.push(s) + "@@";
  60. }
  61. public function quote(s:String):String
  62. {
  63. return "@@HX_ESCAPE" + id + "_" +escapes.push(s) + "@@";
  64. }
  65. public function addValue(s:StringBuf, v:Dynamic)
  66. {
  67. if (Std.is(v, Date))
  68. {
  69. v = Std.string(v);
  70. } else if (Std.is(v, Bytes)) {
  71. var bt:Bytes = v;
  72. v = bt.getData();
  73. }
  74. s.add("@@HX_ESCAPE");
  75. s.add(id);
  76. s.add("_");
  77. s.add(escapes.push(v));
  78. s.add("@@");
  79. }
  80. public function lastInsertId():Int
  81. {
  82. return _lastInsertId;
  83. }
  84. public function dbName():String
  85. {
  86. try {
  87. var ret = cnx.getMetaData().getDriverName();
  88. var retc = ret.toLowerCase();
  89. if (retc.indexOf("mysql") != -1)
  90. return "MySQL";
  91. else if (retc.indexOf("sqlite") != -1)
  92. return "SQLite";
  93. return ret;
  94. } catch(e:Dynamic) { throw e; }
  95. }
  96. public function startTransaction()
  97. {
  98. try
  99. {
  100. cnx.setAutoCommit(false);
  101. }
  102. catch(e:Dynamic) throw e;
  103. }
  104. public function commit()
  105. {
  106. try
  107. {
  108. cnx.commit();
  109. }
  110. catch(e:Dynamic)
  111. {
  112. throw e;
  113. }
  114. }
  115. public function rollback()
  116. {
  117. try
  118. cnx.rollback()
  119. catch(e:Dynamic) throw e;
  120. }
  121. public function request(s:String):sys.db.ResultSet
  122. {
  123. var newst = new StringBuf();
  124. var sentArray = [];
  125. //cycle through the request string, adding any @@HX_ESCAPE@@ reference to the sentArray
  126. var r = escapeRegex;
  127. var myid = id + "", escapes = escapes, elen = escapes.length;
  128. try
  129. {
  130. while (r.match(s))
  131. {
  132. var id = r.matched(1);
  133. if (id != myid) throw "Request quotes are only valid for one single request; They can't be cached.";
  134. newst.add(r.matchedLeft());
  135. var eid = Std.parseInt(r.matched(2));
  136. if (eid == null || eid > elen)
  137. throw "Invalid request quote ID " + eid;
  138. sentArray.push(escapes[eid - 1]);
  139. newst.add("?");
  140. s = r.matchedRight();
  141. }
  142. newst.add(s);
  143. var stmt = cnx.prepareStatement(newst.toString(), java.sql.Statement.Statement_Statics.RETURN_GENERATED_KEYS);
  144. for (i in 0...sentArray.length)
  145. {
  146. stmt.setObject(i + 1, sentArray[i]);
  147. }
  148. var ret = null, dbName = dbName();
  149. if (stmt.execute())
  150. {
  151. //is a result set
  152. var rs = stmt.getResultSet();
  153. ret = new JdbcResultSet(rs, dbName, stmt.getMetaData());
  154. } else {
  155. //is an update
  156. var affected = stmt.getUpdateCount();
  157. if (affected == 1)
  158. {
  159. var autogen = stmt.getGeneratedKeys();
  160. if (autogen.next())
  161. {
  162. this._lastInsertId = autogen.getInt(1);
  163. }
  164. }
  165. ret = new JdbcResultSet(null, dbName,null);
  166. }
  167. if (escapes.length != 0)
  168. escapes = [];
  169. this.id = ids.getAndIncrement();
  170. return ret;
  171. }
  172. catch(e:Dynamic)
  173. {
  174. if (escapes.length != 0)
  175. escapes = [];
  176. this.id = ids.getAndIncrement();
  177. throw e;
  178. }
  179. }
  180. }
  181. @:native('haxe.java.db.JdbcResultSet')
  182. private class JdbcResultSet implements sys.db.ResultSet
  183. {
  184. @:isVar public var length(get,null) : Int;
  185. public var nfields(get,null) : Int;
  186. private var rs:java.sql.ResultSet;
  187. private var names:Array<String>;
  188. private var types:java.NativeArray<Int>;
  189. private var dbName:String;
  190. private var didNext:Bool;
  191. public function new(rs, dbName, meta:java.sql.ResultSetMetaData)
  192. {
  193. this.dbName = dbName;
  194. this.rs = rs;
  195. if (meta != null)
  196. {
  197. try {
  198. var count = meta.getColumnCount();
  199. var names = [], types = new NativeArray(count);
  200. for (i in 0...count)
  201. {
  202. names.push(meta.getColumnName(i+1));
  203. types[i] = meta.getColumnType(i+1);
  204. }
  205. this.types = types;
  206. this.names = names;
  207. } catch(e:Dynamic) throw e;
  208. }
  209. }
  210. private function get_length():Int
  211. {
  212. if (length == 0)
  213. {
  214. try
  215. {
  216. var cur = rs.getRow();
  217. rs.last();
  218. this.length = rs.getRow();
  219. rs.absolute(cur);
  220. } catch(e:Dynamic) throw e;
  221. }
  222. return length;
  223. }
  224. private function get_nfields():Int
  225. {
  226. return names == null ? 0 : names.length;
  227. }
  228. public function hasNext() : Bool
  229. {
  230. try
  231. {
  232. didNext = true;
  233. return rs != null && rs.next();
  234. }
  235. catch(e:Dynamic) { return throw e; }
  236. }
  237. public function next() : Dynamic
  238. {
  239. try {
  240. if (rs == null) return null;
  241. if (didNext)
  242. {
  243. didNext = false;
  244. } else {
  245. if (!rs.next())
  246. {
  247. return null;
  248. }
  249. }
  250. var ret = {}, names = names, types = types;
  251. for (i in 0...names.length)
  252. {
  253. var name = names[i], t = types[i], val:Dynamic = null;
  254. if (t == Types.FLOAT)
  255. {
  256. val = rs.getDouble(i+1);
  257. } else if (t == Types.DATE || t == Types.TIME) {
  258. if (dbName == "SQLite")
  259. {
  260. var str = rs.getString(i+1);
  261. if (str != null)
  262. {
  263. var d:Date = Date.fromString(str);
  264. val = d;
  265. }
  266. } else {
  267. var d:java.sql.Date = rs.getDate(i+1);
  268. if (d != null)
  269. val = Date.fromTime(cast d.getTime());
  270. }
  271. } else if (t == Types.LONGVARBINARY || t == Types.VARBINARY || t == Types.BINARY || t == Types.BLOB) {
  272. var b = rs.getBytes(i+1);
  273. if (b != null)
  274. val = Bytes.ofData(b);
  275. } else {
  276. untyped __java__("val = rs.getObject(i + 1)"); //type parameter constraint + overloads
  277. }
  278. Reflect.setField(ret, name, val);
  279. }
  280. return ret;
  281. } catch(e:Dynamic) throw e;
  282. }
  283. public function results() : List<Dynamic>
  284. {
  285. var l = new List();
  286. if (rs == null) return l;
  287. try
  288. {
  289. while(hasNext())
  290. l.add(next());
  291. } catch(e:Dynamic) throw e;
  292. return l;
  293. }
  294. public function getResult( n : Int ) : String
  295. {
  296. try
  297. {
  298. return rs.getString(n);
  299. } catch(e:Dynamic) throw e;
  300. }
  301. public function getIntResult( n : Int ) : Int
  302. {
  303. try
  304. {
  305. return rs.getInt(n);
  306. }
  307. catch(e:Dynamic) { return throw e; };
  308. }
  309. public function getFloatResult( n : Int ) : Float
  310. {
  311. try
  312. {
  313. return rs.getFloat(n);
  314. } catch(e:Dynamic) { return throw e; };
  315. }
  316. public function getFieldsNames() : Null<Array<String>>
  317. {
  318. return this.names;
  319. }
  320. }