PDO.hx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /*
  2. * Copyright (C)2005-2016 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 php.db;
  23. import php.NativeArray;
  24. import sys.db.Connection;
  25. import sys.db.ResultSet;
  26. import php.Lib;
  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. private class PDOConnection implements Connection {
  81. var pdo : PDOClass;
  82. var dbname : String;
  83. public function new(dsn : String, ?user : String, ?password : String, ?options : Dynamic) {
  84. if(null == options)
  85. pdo = untyped __call__("new PDO", dsn, user, password);
  86. else
  87. {
  88. var arr : NativeArray = untyped __call__("array");
  89. for (key in Reflect.fields(options))
  90. arr[untyped key] = Reflect.field(options, key);
  91. pdo = untyped __call__("new PDO", dsn, user, password, arr);
  92. }
  93. dbname = dsn.split(':').shift();
  94. switch(dbname.toLowerCase())
  95. {
  96. case "sqlite":
  97. dbname = "SQLite";
  98. case "mysql":
  99. dbname = "MySQL";
  100. }
  101. }
  102. public function close() {
  103. pdo = null;
  104. untyped __call__("unset", pdo);
  105. }
  106. public function request( s : String ) : ResultSet {
  107. var result = pdo.query(s, untyped __php__("PDO::PARAM_STR"));
  108. if(untyped __physeq__(result, false))
  109. {
  110. var info = Lib.toHaxeArray(pdo.errorInfo());
  111. throw "Error while executing " + s + " (" + info[2] + ")";
  112. }
  113. var db = dbname.toLowerCase();
  114. switch(db)
  115. {
  116. case "sqlite":
  117. return new AllResultSet(result, new DBNativeStrategy(db));
  118. default: // mysql
  119. return new PDOResultSet(result, new PHPNativeStrategy());
  120. }
  121. }
  122. public function escape( s : String ) {
  123. var output = pdo.quote(s);
  124. return output.length > 2 ? output.substr(1, output.length-2) : output;
  125. }
  126. public function quote( s : String ) {
  127. if( s.indexOf("\000") >= 0 )
  128. return "x'"+base16_encode(s)+"'";
  129. return pdo.quote(s);
  130. }
  131. public function addValue( s : StringBuf, v : Dynamic ) {
  132. if( untyped __call__("is_int", v) || __call__("is_null", v))
  133. s.add(v);
  134. else if( untyped __call__("is_bool", v) )
  135. s.add(if( v ) 1 else 0);
  136. else
  137. s.add(quote(Std.string(v)));
  138. }
  139. public function lastInsertId() {
  140. return cast(Std.parseInt(pdo.lastInsertId()), Int);
  141. }
  142. public function dbName() {
  143. return dbname;
  144. }
  145. public function startTransaction() {
  146. pdo.beginTransaction();
  147. }
  148. public function commit() {
  149. pdo.commit();
  150. }
  151. public function rollback() {
  152. pdo.rollBack();
  153. }
  154. function base16_encode(str : String) {
  155. str = untyped __call__("unpack", "H"+(2 * str.length), str);
  156. str = untyped __call__("chunk_split", untyped str[1]);
  157. return str;
  158. }
  159. }
  160. private class TypeStrategy {
  161. public function new() {
  162. }
  163. public function map(data : NativeArray) : Dynamic
  164. {
  165. return throw "must override";
  166. }
  167. public static function convert(v : String, type : String) : Dynamic {
  168. if (v == null) return v;
  169. switch(type) {
  170. case "bool":
  171. return untyped __call__("(bool)", v);
  172. case "int":
  173. return untyped __call__("intval", v);
  174. case "float":
  175. return untyped __call__("floatval", v);
  176. case "date":
  177. return Date.fromString(v);
  178. case "blob":
  179. return haxe.io.Bytes.ofString(v);
  180. default:
  181. return v;
  182. }
  183. }
  184. }
  185. private class PHPNativeStrategy extends TypeStrategy {
  186. static inline var KEY = "native_type";
  187. override function map(data : NativeArray) : Dynamic {
  188. if (!untyped __call__("isset", data[KEY])) {
  189. if (untyped __call__("isset", data["precision"])) {
  190. // if (untyped __call__("isset", data["len"]) && data["len"] == 1)
  191. // return "bool"
  192. // else
  193. return "int";
  194. } else
  195. return "string";
  196. }
  197. var pdo_type_str:Int = untyped __php__("PDO::PARAM_STR");
  198. var pdo_type : Int = untyped data["pdo_type"];
  199. var type : String = untyped data[KEY];
  200. type = type.toLowerCase();
  201. switch(type)
  202. {
  203. case "float", "decimal", "double", "newdecimal":
  204. return "float";
  205. case "date", "datetime", "timestamp":
  206. return "date";
  207. case "bool", "tinyint(1)":
  208. return "bool";
  209. case "int", "int24", "int32", "long", "longlong", "short", "tiny":
  210. return "int";
  211. case "blob" if (pdo_type == pdo_type_str):
  212. return "string";
  213. case "blob":
  214. return "blob";
  215. default:
  216. return "string";
  217. }
  218. }
  219. }
  220. private class DBNativeStrategy extends PHPNativeStrategy {
  221. static inline var SUFFIX = ":decl_type";
  222. var dbname : String;
  223. var key : String;
  224. public function new(dbname : String)
  225. {
  226. super();
  227. this.dbname = dbname.toLowerCase();
  228. this.key = dbname + SUFFIX;
  229. }
  230. override function map(data : NativeArray) : Dynamic {
  231. if (!untyped __call__("isset", data[key]))
  232. return super.map(data);
  233. var type : String = untyped data[key];
  234. type = type.toLowerCase();
  235. switch(type)
  236. {
  237. case "real":
  238. return "float";
  239. case "integer":
  240. return "int";
  241. default:
  242. return "string";
  243. }
  244. }
  245. }
  246. private class BaseResultSet implements ResultSet {
  247. var pdo : PDOStatement;
  248. var typeStrategy : TypeStrategy;
  249. var _fields : Int;
  250. var _columnNames : Array<String>;
  251. var _columnTypes : Array<String>;
  252. public var length(get, null) : Int;
  253. public var nfields(get, null) : Int;
  254. public function new(pdo : PDOStatement, typeStrategy : TypeStrategy)
  255. {
  256. this.pdo = pdo;
  257. this.typeStrategy = typeStrategy;
  258. this._fields = pdo.columnCount();
  259. this._columnNames = [];
  260. this._columnTypes = [];
  261. feedColumns();
  262. }
  263. private function feedColumns() {
  264. for (i in 0..._fields) {
  265. var data = pdo.getColumnMeta(i);
  266. _columnNames.push(data[untyped 'name']);
  267. _columnTypes.push(typeStrategy.map(data));
  268. }
  269. }
  270. public function getFloatResult(index : Int) : Float {
  271. return untyped __call__("floatval", getResult(index));
  272. }
  273. public function getIntResult(index : Int) : Int {
  274. return untyped __call__("intval", getResult(index));
  275. }
  276. public function getResult(index : Int) : String {
  277. return throw "must override";
  278. }
  279. public function hasNext() : Bool {
  280. return throw "must override";
  281. }
  282. function get_length() : Int {
  283. return throw "must override";
  284. }
  285. function nextRow() : NativeArray {
  286. return throw "must override";
  287. }
  288. public function next() : Dynamic {
  289. var row = nextRow();
  290. var o : Dynamic = { };
  291. for (i in 0..._fields)
  292. Reflect.setField(o, _columnNames[i], TypeStrategy.convert(row[i], _columnTypes[i]));
  293. return o;
  294. }
  295. function get_nfields() : Int {
  296. return _fields;
  297. }
  298. public function results() : List<Dynamic>
  299. {
  300. var list = new List();
  301. while (hasNext())
  302. list.add(next());
  303. return list;
  304. }
  305. public function getFieldsNames() : Array<String> {
  306. return throw "Not implemented";
  307. }
  308. }
  309. private class AllResultSet extends BaseResultSet {
  310. var all : NativeArray;
  311. var pos : Int;
  312. var _length : Int;
  313. public function new(pdo : PDOStatement, typeStrategy : TypeStrategy)
  314. {
  315. super(pdo, typeStrategy);
  316. this.all = pdo.fetchAll(untyped __php__("PDO::FETCH_NUM"));
  317. this.pos = 0;
  318. this._length = untyped __call__("count", all);
  319. }
  320. override function getResult(index : Int) : String {
  321. untyped if(__call__("isset", all[0]) && __call__("isset", all[0][index]))
  322. return all[0][index];
  323. else
  324. return null;
  325. }
  326. override function hasNext() : Bool {
  327. return pos < _length;
  328. }
  329. override function get_length() : Int {
  330. return _length;
  331. }
  332. override function nextRow() : NativeArray {
  333. return all[pos++];
  334. }
  335. }
  336. private class PDOResultSet extends BaseResultSet {
  337. private var cache : NativeArray;
  338. public function new(pdo : PDOStatement, typeStrategy : TypeStrategy)
  339. {
  340. super(pdo, typeStrategy);
  341. }
  342. override function getResult(index : Int) : String {
  343. if (!hasNext())
  344. return null;
  345. return cache[index];
  346. }
  347. override function hasNext() {
  348. if(untyped __physeq__(null, cache))
  349. cacheRow();
  350. return (untyped cache);
  351. }
  352. override function get_length() {
  353. if (untyped __physeq__(pdo, false))
  354. return 0;
  355. return pdo.rowCount();
  356. }
  357. private function cacheRow() {
  358. cache = untyped pdo.fetch(__php__("PDO::FETCH_NUM"), __php__("PDO::FETCH_ORI_NEXT"));
  359. }
  360. override function nextRow()
  361. {
  362. if (!hasNext())
  363. return null;
  364. else {
  365. var v = cache;
  366. cache = null;
  367. return v;
  368. }
  369. }
  370. }