PDO.hx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*
  2. * Copyright (c) 2005, The haXe Project Contributors
  3. * All rights reserved.
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. *
  7. * - Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * - Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
  17. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  20. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  23. * DAMAGE.
  24. */
  25. package php.db;
  26. import php.NativeArray;
  27. /**
  28. * PDO::FETCH_COLUMN = 7
  29. * PDO::FETCH_CLASS = 8
  30. * PDO::FETCH_INTO = 9
  31. * PDO::PARAM_STR = 2
  32. * PDO::FETCH_BOTH = 4
  33. * PDO::FETCH_ORI_NEXT = 0
  34. */
  35. class PDO
  36. {
  37. public static function open(dsn : String, ?user : String, ?password : String, ?options : Dynamic) : Connection {
  38. return new PDOConnection(dsn, user, password, options);
  39. }
  40. }
  41. extern class PDOClass
  42. {
  43. // public function new(dns : String, ?username : String, ?password : String, ?driver_options : NativeArray) : Void;
  44. public function beginTransaction() : Bool;
  45. public function commit() : Bool;
  46. public function errorCode() : Dynamic;
  47. public function errorInfo() : NativeArray;
  48. public function exec(statement : String) : Int;
  49. public function getAttribute(attribute : Int) : Dynamic;
  50. public function getAvailableDrivers() : NativeArray;
  51. public function lastInsertId(?name : String) : String;
  52. public function prepare(statement : String, driver_options : NativeArray) : PDOStatement;
  53. public function query(statement : String, mode : Int) : PDOStatement;
  54. public function quote(String : String, ?parameter_type : Int = 2) : String;
  55. public function rollBack() : Bool;
  56. public function setAttribute(attribute : Int, value : Dynamic) : Bool;
  57. }
  58. extern class PDOStatement
  59. {
  60. public function bindColumn(column : Dynamic, param : Dynamic, ?type : Int, ?maxlen : Int, ?driverdata : Dynamic) : Bool;
  61. public function bindParam(parameter : Dynamic, variable : Dynamic, ?data_type : Int, ?length : Int, ?driver_options : Dynamic) : Bool;
  62. public function bindValue(parameter : Dynamic, value : Dynamic, ?data_type : Int) : Bool;
  63. public function closeCursor() : Bool;
  64. public function columnCount() : Int;
  65. public function debugDumpParams() : Bool;
  66. public function errorCode() : String;
  67. public function errorInfo() : NativeArray;
  68. public function execute(input_parameters : NativeArray) : Bool;
  69. public function fetch(?fetch_style : Int = 4, ?cursor_orientation : Int = 0, ?cursor_offset : Int = 0) : Dynamic;
  70. public function fetchAll(?fetch_style : Int) : NativeArray;
  71. public function fetchColumn(?column_number : Int = 0) : String;
  72. public function fetchObject(?class_name : String, ?ctor_args : NativeArray) : Dynamic;
  73. public function getAttribute(attribute : Int) : Dynamic;
  74. public function getColumnMeta(column : Int) : NativeArray;
  75. public function nextRowset() : Bool;
  76. public function rowCount() : Int;
  77. public function setAttribute(attribute : Int, value : Dynamic) : Bool;
  78. public function setFetchMode(mode : Int, ?fetch : Dynamic, ?ctorargs : NativeArray) : Bool;
  79. }
  80. import php.Lib;
  81. private class PDOConnection implements Connection {
  82. var pdo : PDOClass;
  83. var dbname : String;
  84. public function new(dsn : String, ?user : String, ?password : String, ?options : Dynamic) {
  85. if(null == options)
  86. pdo = untyped __call__("new PDO", dsn, user, password);
  87. else
  88. {
  89. var arr : NativeArray = untyped __call__("array");
  90. for (key in Reflect.fields(options))
  91. arr[untyped key] = Reflect.field(options, key);
  92. pdo = untyped __call__("new PDO", dsn, user, password, arr);
  93. }
  94. dbname = dsn.split(':').shift();
  95. }
  96. public function close() {
  97. pdo = null;
  98. untyped __call__("unset", pdo);
  99. }
  100. public function request( s : String ) : php.db.ResultSet {
  101. var result = pdo.query(s, untyped __php__("PDO::PARAM_STR"));
  102. if(untyped __physeq__(result, false))
  103. {
  104. var info = Lib.toHaxeArray(pdo.errorInfo());
  105. throw "Error while executing " + s + " (" + info[2] + ")";
  106. }
  107. var db = dbname.toLowerCase();
  108. switch(db)
  109. {
  110. case "sqlite":
  111. return new AllResultSet(result, new DBNativeStrategy(db));
  112. default: // mysql
  113. return new PDOResultSet(result, new PHPNativeStrategy());
  114. }
  115. }
  116. public function escape( s : String ) {
  117. var output = pdo.quote(s);
  118. return output.length > 2 ? output.substr(1, output.length-2) : output;
  119. }
  120. public function quote( s : String ) {
  121. if( s.indexOf("\000") >= 0 )
  122. return "x'"+base16_encode(s)+"'";
  123. return pdo.quote(s);
  124. }
  125. public function addValue( s : StringBuf, v : Dynamic ) {
  126. if( untyped __call__("is_int", v) || __call__("is_null", v))
  127. s.add(v);
  128. else if( untyped __call__("is_bool", v) )
  129. s.add(if( v ) 1 else 0);
  130. else
  131. s.add(quote(Std.string(v)));
  132. }
  133. public function lastInsertId() {
  134. return cast(Std.parseInt(pdo.lastInsertId()), Int);
  135. }
  136. public function dbName() {
  137. return dbname;
  138. }
  139. public function startTransaction() {
  140. pdo.beginTransaction();
  141. }
  142. public function commit() {
  143. pdo.commit();
  144. }
  145. public function rollback() {
  146. pdo.rollBack();
  147. }
  148. function base16_encode(str : String) {
  149. str = untyped __call__("unpack", "H"+(2 * str.length), str);
  150. str = untyped __call__("chunk_split", untyped str[1]);
  151. return str;
  152. }
  153. }
  154. private class TypeStrategy {
  155. public function new() {
  156. }
  157. public function map(data : NativeArray) : Dynamic
  158. {
  159. return throw "must override";
  160. }
  161. public static function convert(v : String, type : String) : Dynamic {
  162. if (v == null) return v;
  163. switch(type) {
  164. case "bool":
  165. return untyped __call__("(bool)", v);
  166. case "int":
  167. return untyped __call__("intval", v);
  168. case "float":
  169. return untyped __call__("floatval", v);
  170. case "date":
  171. return Date.fromString(v);
  172. default:
  173. return v;
  174. }
  175. }
  176. }
  177. private class PHPNativeStrategy extends TypeStrategy {
  178. static inline var KEY = "native_type";
  179. override function map(data : NativeArray) : Dynamic {
  180. if (!untyped __call__("isset", data[KEY])) {
  181. if (untyped __call__("isset", data["precision"])) {
  182. // if (untyped __call__("isset", data["len"]) && data["len"] == 1)
  183. // return "bool"
  184. // else
  185. return "int";
  186. } else
  187. return "string";
  188. }
  189. var type : String = untyped data[KEY];
  190. type = type.toLowerCase();
  191. switch(type)
  192. {
  193. case "float", "decimal", "double", "newdecimal":
  194. return "float";
  195. case "date", "datetime":
  196. return "date";
  197. case "bool":
  198. return "bool";
  199. case "int", "int24", "int32", "long", "longlong", "short":
  200. return "int";
  201. default:
  202. return "string";
  203. }
  204. }
  205. }
  206. private class DBNativeStrategy extends PHPNativeStrategy {
  207. static inline var SUFFIX = ":decl_type";
  208. var dbname : String;
  209. var key : String;
  210. public function new(dbname : String)
  211. {
  212. super();
  213. this.dbname = dbname.toLowerCase();
  214. this.key = dbname + SUFFIX;
  215. }
  216. override function map(data : NativeArray) : Dynamic {
  217. if (!untyped __call__("isset", data[key]))
  218. return super.map(data);
  219. var type : String = untyped data[key];
  220. type = type.toLowerCase();
  221. switch(type)
  222. {
  223. case "real":
  224. return "float";
  225. case "integer":
  226. return "int";
  227. default:
  228. return "string";
  229. }
  230. }
  231. }
  232. private class BaseResultSet implements php.db.ResultSet {
  233. var pdo : PDOStatement;
  234. var typeStrategy : TypeStrategy;
  235. var _fields : Int;
  236. var _columnNames : Array<String>;
  237. var _columnTypes : Array<String>;
  238. public var length(getLength, null) : Int;
  239. public var nfields(getNFields, null) : Int;
  240. public function new(pdo : PDOStatement, typeStrategy : TypeStrategy)
  241. {
  242. this.pdo = pdo;
  243. this.typeStrategy = typeStrategy;
  244. this._fields = pdo.columnCount();
  245. this._columnNames = [];
  246. this._columnTypes = [];
  247. feedColumns();
  248. }
  249. private function feedColumns() {
  250. for (i in 0..._fields) {
  251. var data = pdo.getColumnMeta(i);
  252. _columnNames.push(data[untyped 'name']);
  253. _columnTypes.push(typeStrategy.map(data));
  254. }
  255. }
  256. public function getFloatResult(index : Int) : Float {
  257. return untyped __call__("floatval", getResult(index));
  258. }
  259. public function getIntResult(index : Int) : Int {
  260. return untyped __call__("intval", getResult(index));
  261. }
  262. public function getResult(index : Int) : String {
  263. return throw "must override";
  264. }
  265. public function hasNext() : Bool {
  266. return throw "must override";
  267. }
  268. function getLength() : Int {
  269. return throw "must override";
  270. }
  271. function nextRow() : NativeArray {
  272. return throw "must override";
  273. }
  274. public function next() : Dynamic {
  275. var row = nextRow();
  276. var o : Dynamic = { };
  277. for (i in 0..._fields)
  278. Reflect.setField(o, _columnNames[i], TypeStrategy.convert(row[i], _columnTypes[i]));
  279. return o;
  280. }
  281. function getNFields() : Int {
  282. return _fields;
  283. }
  284. public function results() : List<Dynamic>
  285. {
  286. var list = new List();
  287. while (hasNext())
  288. list.add(next());
  289. return list;
  290. }
  291. public function getFieldsNames() : Array<String> {
  292. return throw "Not implemented";
  293. }
  294. }
  295. private class AllResultSet extends BaseResultSet {
  296. var all : NativeArray;
  297. var pos : Int;
  298. var _length : Int;
  299. public function new(pdo : PDOStatement, typeStrategy : TypeStrategy)
  300. {
  301. super(pdo, typeStrategy);
  302. this.all = pdo.fetchAll(untyped __php__("PDO::FETCH_NUM"));
  303. this.pos = 0;
  304. this._length = untyped __call__("count", all);
  305. }
  306. override function getResult(index : Int) : String {
  307. untyped if(__call__("isset", all[0]) && __call__("isset", all[0][index]))
  308. return all[0][index];
  309. else
  310. return null;
  311. }
  312. override function hasNext() : Bool {
  313. return pos < _length;
  314. }
  315. override function getLength() : Int {
  316. return _length;
  317. }
  318. override function nextRow() : NativeArray {
  319. return all[pos++];
  320. }
  321. }
  322. private class PDOResultSet extends BaseResultSet {
  323. private var cache : NativeArray;
  324. public function new(pdo : PDOStatement, typeStrategy : TypeStrategy)
  325. {
  326. super(pdo, typeStrategy);
  327. }
  328. override function getResult(index : Int) : String {
  329. if (!hasNext())
  330. return null;
  331. return cache[index];
  332. }
  333. override function hasNext() {
  334. if(untyped __physeq__(null, cache))
  335. cacheRow();
  336. return (untyped cache);
  337. }
  338. override function getLength() {
  339. if (untyped __physeq__(pdo, false))
  340. return 0;
  341. return pdo.rowCount();
  342. }
  343. private function cacheRow() {
  344. cache = untyped pdo.fetch(__php__("PDO::FETCH_NUM"), __php__("PDO::FETCH_ORI_NEXT"));
  345. }
  346. override function nextRow()
  347. {
  348. if (!hasNext())
  349. return null;
  350. else {
  351. var v = cache;
  352. cache = null;
  353. return v;
  354. }
  355. }
  356. }