Jdbc.hx 6.5 KB

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