mysqli.php 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907
  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
  7. // | Stig. S. Bakken, Lukas Smith |
  8. // | All rights reserved. |
  9. // +----------------------------------------------------------------------+
  10. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
  11. // | API as well as database abstraction for PHP applications. |
  12. // | This LICENSE is in the BSD license style. |
  13. // | |
  14. // | Redistribution and use in source and binary forms, with or without |
  15. // | modification, are permitted provided that the following conditions |
  16. // | are met: |
  17. // | |
  18. // | Redistributions of source code must retain the above copyright |
  19. // | notice, this list of conditions and the following disclaimer. |
  20. // | |
  21. // | Redistributions in binary form must reproduce the above copyright |
  22. // | notice, this list of conditions and the following disclaimer in the |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // | |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission. |
  29. // | |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
  41. // | POSSIBILITY OF SUCH DAMAGE. |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Lukas Smith <smith@pooteeweet.org> |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id$
  47. //
  48. /**
  49. * MDB2 MySQLi driver
  50. *
  51. * @package MDB2
  52. * @category Database
  53. * @author Lukas Smith <smith@pooteeweet.org>
  54. */
  55. class MDB2_Driver_mysqli extends MDB2_Driver_Common
  56. {
  57. // {{{ properties
  58. public $string_quoting = array(
  59. 'start' => "'",
  60. 'end' => "'",
  61. 'escape' => '\\',
  62. 'escape_pattern' => '\\',
  63. );
  64. public $identifier_quoting = array(
  65. 'start' => '`',
  66. 'end' => '`',
  67. 'escape' => '`',
  68. );
  69. /**
  70. * The ouptut of mysqli_errno() in _doQuery(), if any.
  71. * @var integer
  72. */
  73. protected $_query_errno;
  74. /**
  75. * The ouptut of mysqli_error() in _doQuery(), if any.
  76. * @var string
  77. */
  78. protected $_query_error;
  79. public $sql_comments = array(
  80. array('start' => '-- ', 'end' => "\n", 'escape' => false),
  81. array('start' => '#', 'end' => "\n", 'escape' => false),
  82. array('start' => '/*', 'end' => '*/', 'escape' => false),
  83. );
  84. protected $server_capabilities_checked = false;
  85. protected $start_transaction = false;
  86. public $varchar_max_length = 255;
  87. // }}}
  88. // {{{ constructor
  89. /**
  90. * Constructor
  91. */
  92. function __construct()
  93. {
  94. parent::__construct();
  95. $this->phptype = 'mysqli';
  96. $this->dbsyntax = 'mysql';
  97. $this->supported['sequences'] = 'emulated';
  98. $this->supported['indexes'] = true;
  99. $this->supported['affected_rows'] = true;
  100. $this->supported['transactions'] = false;
  101. $this->supported['savepoints'] = false;
  102. $this->supported['summary_functions'] = true;
  103. $this->supported['order_by_text'] = true;
  104. $this->supported['current_id'] = 'emulated';
  105. $this->supported['limit_queries'] = true;
  106. $this->supported['LOBs'] = true;
  107. $this->supported['replace'] = true;
  108. $this->supported['sub_selects'] = 'emulated';
  109. $this->supported['triggers'] = false;
  110. $this->supported['auto_increment'] = true;
  111. $this->supported['primary_key'] = true;
  112. $this->supported['result_introspection'] = true;
  113. $this->supported['prepared_statements'] = 'emulated';
  114. $this->supported['identifier_quoting'] = true;
  115. $this->supported['pattern_escaping'] = true;
  116. $this->supported['new_link'] = true;
  117. $this->options['DBA_username'] = false;
  118. $this->options['DBA_password'] = false;
  119. $this->options['default_table_type'] = '';
  120. $this->options['multi_query'] = false;
  121. $this->options['max_identifiers_length'] = 64;
  122. $this->_reCheckSupportedOptions();
  123. }
  124. // }}}
  125. // {{{ _reCheckSupportedOptions()
  126. /**
  127. * If the user changes certain options, other capabilities may depend
  128. * on the new settings, so we need to check them (again).
  129. *
  130. * @access private
  131. */
  132. function _reCheckSupportedOptions()
  133. {
  134. $this->supported['transactions'] = $this->options['use_transactions'];
  135. $this->supported['savepoints'] = $this->options['use_transactions'];
  136. if ($this->options['default_table_type']) {
  137. switch (strtoupper($this->options['default_table_type'])) {
  138. case 'BLACKHOLE':
  139. case 'MEMORY':
  140. case 'ARCHIVE':
  141. case 'CSV':
  142. case 'HEAP':
  143. case 'ISAM':
  144. case 'MERGE':
  145. case 'MRG_ISAM':
  146. case 'ISAM':
  147. case 'MRG_MYISAM':
  148. case 'MYISAM':
  149. $this->supported['savepoints'] = false;
  150. $this->supported['transactions'] = false;
  151. $this->warnings[] = $this->options['default_table_type'] .
  152. ' is not a supported default table type';
  153. break;
  154. }
  155. }
  156. }
  157. // }}}
  158. // {{{ function setOption($option, $value)
  159. /**
  160. * set the option for the db class
  161. *
  162. * @param string option name
  163. * @param mixed value for the option
  164. *
  165. * @return mixed MDB2_OK or MDB2 Error Object
  166. *
  167. * @access public
  168. */
  169. function setOption($option, $value)
  170. {
  171. $res = parent::setOption($option, $value);
  172. $this->_reCheckSupportedOptions();
  173. }
  174. // }}}
  175. // {{{ errorInfo()
  176. /**
  177. * This method is used to collect information about an error
  178. *
  179. * @param integer $error
  180. * @return array
  181. * @access public
  182. */
  183. function errorInfo($error = null)
  184. {
  185. if ($this->_query_errno) {
  186. $native_code = $this->_query_errno;
  187. $native_msg = $this->_query_error;
  188. } elseif ($this->connection) {
  189. $native_code = @mysqli_errno($this->connection);
  190. $native_msg = @mysqli_error($this->connection);
  191. } else {
  192. $native_code = @mysqli_connect_errno();
  193. $native_msg = @mysqli_connect_error();
  194. }
  195. if (null === $error) {
  196. static $ecode_map;
  197. if (empty($ecode_map)) {
  198. $ecode_map = array(
  199. 1000 => MDB2_ERROR_INVALID, //hashchk
  200. 1001 => MDB2_ERROR_INVALID, //isamchk
  201. 1004 => MDB2_ERROR_CANNOT_CREATE,
  202. 1005 => MDB2_ERROR_CANNOT_CREATE,
  203. 1006 => MDB2_ERROR_CANNOT_CREATE,
  204. 1007 => MDB2_ERROR_ALREADY_EXISTS,
  205. 1008 => MDB2_ERROR_CANNOT_DROP,
  206. 1009 => MDB2_ERROR_CANNOT_DROP,
  207. 1010 => MDB2_ERROR_CANNOT_DROP,
  208. 1011 => MDB2_ERROR_CANNOT_DELETE,
  209. 1022 => MDB2_ERROR_ALREADY_EXISTS,
  210. 1029 => MDB2_ERROR_NOT_FOUND,
  211. 1032 => MDB2_ERROR_NOT_FOUND,
  212. 1044 => MDB2_ERROR_ACCESS_VIOLATION,
  213. 1045 => MDB2_ERROR_ACCESS_VIOLATION,
  214. 1046 => MDB2_ERROR_NODBSELECTED,
  215. 1048 => MDB2_ERROR_CONSTRAINT,
  216. 1049 => MDB2_ERROR_NOSUCHDB,
  217. 1050 => MDB2_ERROR_ALREADY_EXISTS,
  218. 1051 => MDB2_ERROR_NOSUCHTABLE,
  219. 1054 => MDB2_ERROR_NOSUCHFIELD,
  220. 1060 => MDB2_ERROR_ALREADY_EXISTS,
  221. 1061 => MDB2_ERROR_ALREADY_EXISTS,
  222. 1062 => MDB2_ERROR_ALREADY_EXISTS,
  223. 1064 => MDB2_ERROR_SYNTAX,
  224. 1067 => MDB2_ERROR_INVALID,
  225. 1072 => MDB2_ERROR_NOT_FOUND,
  226. 1086 => MDB2_ERROR_ALREADY_EXISTS,
  227. 1091 => MDB2_ERROR_NOT_FOUND,
  228. 1100 => MDB2_ERROR_NOT_LOCKED,
  229. 1109 => MDB2_ERROR_NOT_FOUND,
  230. 1125 => MDB2_ERROR_ALREADY_EXISTS,
  231. 1136 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  232. 1138 => MDB2_ERROR_INVALID,
  233. 1142 => MDB2_ERROR_ACCESS_VIOLATION,
  234. 1143 => MDB2_ERROR_ACCESS_VIOLATION,
  235. 1146 => MDB2_ERROR_NOSUCHTABLE,
  236. 1149 => MDB2_ERROR_SYNTAX,
  237. 1169 => MDB2_ERROR_CONSTRAINT,
  238. 1176 => MDB2_ERROR_NOT_FOUND,
  239. 1177 => MDB2_ERROR_NOSUCHTABLE,
  240. 1213 => MDB2_ERROR_DEADLOCK,
  241. 1216 => MDB2_ERROR_CONSTRAINT,
  242. 1217 => MDB2_ERROR_CONSTRAINT,
  243. 1227 => MDB2_ERROR_ACCESS_VIOLATION,
  244. 1235 => MDB2_ERROR_CANNOT_CREATE,
  245. 1299 => MDB2_ERROR_INVALID_DATE,
  246. 1300 => MDB2_ERROR_INVALID,
  247. 1304 => MDB2_ERROR_ALREADY_EXISTS,
  248. 1305 => MDB2_ERROR_NOT_FOUND,
  249. 1306 => MDB2_ERROR_CANNOT_DROP,
  250. 1307 => MDB2_ERROR_CANNOT_CREATE,
  251. 1334 => MDB2_ERROR_CANNOT_ALTER,
  252. 1339 => MDB2_ERROR_NOT_FOUND,
  253. 1356 => MDB2_ERROR_INVALID,
  254. 1359 => MDB2_ERROR_ALREADY_EXISTS,
  255. 1360 => MDB2_ERROR_NOT_FOUND,
  256. 1363 => MDB2_ERROR_NOT_FOUND,
  257. 1365 => MDB2_ERROR_DIVZERO,
  258. 1451 => MDB2_ERROR_CONSTRAINT,
  259. 1452 => MDB2_ERROR_CONSTRAINT,
  260. 1542 => MDB2_ERROR_CANNOT_DROP,
  261. 1546 => MDB2_ERROR_CONSTRAINT,
  262. 1582 => MDB2_ERROR_CONSTRAINT,
  263. 2003 => MDB2_ERROR_CONNECT_FAILED,
  264. 2019 => MDB2_ERROR_INVALID,
  265. );
  266. }
  267. if ($this->options['portability'] & MDB2_PORTABILITY_ERRORS) {
  268. $ecode_map[1022] = MDB2_ERROR_CONSTRAINT;
  269. $ecode_map[1048] = MDB2_ERROR_CONSTRAINT_NOT_NULL;
  270. $ecode_map[1062] = MDB2_ERROR_CONSTRAINT;
  271. } else {
  272. // Doing this in case mode changes during runtime.
  273. $ecode_map[1022] = MDB2_ERROR_ALREADY_EXISTS;
  274. $ecode_map[1048] = MDB2_ERROR_CONSTRAINT;
  275. $ecode_map[1062] = MDB2_ERROR_ALREADY_EXISTS;
  276. }
  277. if (isset($ecode_map[$native_code])) {
  278. $error = $ecode_map[$native_code];
  279. }
  280. }
  281. return array($error, $native_code, $native_msg);
  282. }
  283. // }}}
  284. // {{{ escape()
  285. /**
  286. * Quotes a string so it can be safely used in a query. It will quote
  287. * the text so it can safely be used within a query.
  288. *
  289. * @param string the input string to quote
  290. * @param bool escape wildcards
  291. *
  292. * @return string quoted string
  293. *
  294. * @access public
  295. */
  296. function escape($text, $escape_wildcards = false)
  297. {
  298. if ($escape_wildcards) {
  299. $text = $this->escapePattern($text);
  300. }
  301. $connection = $this->getConnection();
  302. if (MDB2::isError($connection)) {
  303. return $connection;
  304. }
  305. $text = @mysqli_real_escape_string($connection, $text);
  306. return $text;
  307. }
  308. // }}}
  309. // {{{ beginTransaction()
  310. /**
  311. * Start a transaction or set a savepoint.
  312. *
  313. * @param string name of a savepoint to set
  314. * @return mixed MDB2_OK on success, a MDB2 error on failure
  315. *
  316. * @access public
  317. */
  318. function beginTransaction($savepoint = null)
  319. {
  320. $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  321. $this->_getServerCapabilities();
  322. if (null !== $savepoint) {
  323. if (!$this->supports('savepoints')) {
  324. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  325. 'savepoints are not supported', __FUNCTION__);
  326. }
  327. if (!$this->in_transaction) {
  328. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  329. 'savepoint cannot be released when changes are auto committed', __FUNCTION__);
  330. }
  331. $query = 'SAVEPOINT '.$savepoint;
  332. return $this->_doQuery($query, true);
  333. }
  334. if ($this->in_transaction) {
  335. return MDB2_OK; //nothing to do
  336. }
  337. $query = $this->start_transaction ? 'START TRANSACTION' : 'SET AUTOCOMMIT = 0';
  338. $result = $this->_doQuery($query, true);
  339. if (MDB2::isError($result)) {
  340. return $result;
  341. }
  342. $this->in_transaction = true;
  343. return MDB2_OK;
  344. }
  345. // }}}
  346. // {{{ commit()
  347. /**
  348. * Commit the database changes done during a transaction that is in
  349. * progress or release a savepoint. This function may only be called when
  350. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  351. * transaction is implicitly started after committing the pending changes.
  352. *
  353. * @param string name of a savepoint to release
  354. * @return mixed MDB2_OK on success, a MDB2 error on failure
  355. *
  356. * @access public
  357. */
  358. function commit($savepoint = null)
  359. {
  360. $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  361. if (!$this->in_transaction) {
  362. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  363. 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
  364. }
  365. if (null !== $savepoint) {
  366. if (!$this->supports('savepoints')) {
  367. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  368. 'savepoints are not supported', __FUNCTION__);
  369. }
  370. $server_info = $this->getServerVersion();
  371. if (version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '5.0.3', '<')) {
  372. return MDB2_OK;
  373. }
  374. $query = 'RELEASE SAVEPOINT '.$savepoint;
  375. return $this->_doQuery($query, true);
  376. }
  377. if (!$this->supports('transactions')) {
  378. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  379. 'transactions are not supported', __FUNCTION__);
  380. }
  381. $result = $this->_doQuery('COMMIT', true);
  382. if (MDB2::isError($result)) {
  383. return $result;
  384. }
  385. if (!$this->start_transaction) {
  386. $query = 'SET AUTOCOMMIT = 1';
  387. $result = $this->_doQuery($query, true);
  388. if (MDB2::isError($result)) {
  389. return $result;
  390. }
  391. }
  392. $this->in_transaction = false;
  393. return MDB2_OK;
  394. }
  395. // }}}
  396. // {{{ rollback()
  397. /**
  398. * Cancel any database changes done during a transaction or since a specific
  399. * savepoint that is in progress. This function may only be called when
  400. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  401. * transaction is implicitly started after canceling the pending changes.
  402. *
  403. * @param string name of a savepoint to rollback to
  404. * @return mixed MDB2_OK on success, a MDB2 error on failure
  405. *
  406. * @access public
  407. */
  408. function rollback($savepoint = null)
  409. {
  410. $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  411. if (!$this->in_transaction) {
  412. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  413. 'rollback cannot be done changes are auto committed', __FUNCTION__);
  414. }
  415. if (null !== $savepoint) {
  416. if (!$this->supports('savepoints')) {
  417. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  418. 'savepoints are not supported', __FUNCTION__);
  419. }
  420. $query = 'ROLLBACK TO SAVEPOINT '.$savepoint;
  421. return $this->_doQuery($query, true);
  422. }
  423. $query = 'ROLLBACK';
  424. $result = $this->_doQuery($query, true);
  425. if (MDB2::isError($result)) {
  426. return $result;
  427. }
  428. if (!$this->start_transaction) {
  429. $query = 'SET AUTOCOMMIT = 1';
  430. $result = $this->_doQuery($query, true);
  431. if (MDB2::isError($result)) {
  432. return $result;
  433. }
  434. }
  435. $this->in_transaction = false;
  436. return MDB2_OK;
  437. }
  438. // }}}
  439. // {{{ function setTransactionIsolation()
  440. /**
  441. * Set the transacton isolation level.
  442. *
  443. * @param string standard isolation level
  444. * READ UNCOMMITTED (allows dirty reads)
  445. * READ COMMITTED (prevents dirty reads)
  446. * REPEATABLE READ (prevents nonrepeatable reads)
  447. * SERIALIZABLE (prevents phantom reads)
  448. * @param array some transaction options:
  449. * 'wait' => 'WAIT' | 'NO WAIT'
  450. * 'rw' => 'READ WRITE' | 'READ ONLY'
  451. *
  452. * @return mixed MDB2_OK on success, a MDB2 error on failure
  453. *
  454. * @access public
  455. * @since 2.1.1
  456. */
  457. function setTransactionIsolation($isolation, $options = array())
  458. {
  459. $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
  460. if (!$this->supports('transactions')) {
  461. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  462. 'transactions are not supported', __FUNCTION__);
  463. }
  464. switch ($isolation) {
  465. case 'READ UNCOMMITTED':
  466. case 'READ COMMITTED':
  467. case 'REPEATABLE READ':
  468. case 'SERIALIZABLE':
  469. break;
  470. default:
  471. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  472. 'isolation level is not supported: '.$isolation, __FUNCTION__);
  473. }
  474. $query = "SET SESSION TRANSACTION ISOLATION LEVEL $isolation";
  475. return $this->_doQuery($query, true);
  476. }
  477. // }}}
  478. // {{{ _doConnect()
  479. /**
  480. * do the grunt work of the connect
  481. *
  482. * @return connection on success or MDB2 Error Object on failure
  483. * @access protected
  484. */
  485. function _doConnect($username, $password, $persistent = false)
  486. {
  487. if (!extension_loaded($this->phptype)) {
  488. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  489. 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
  490. }
  491. $connection = @mysqli_init();
  492. if (!empty($this->dsn['charset']) && defined('MYSQLI_SET_CHARSET_NAME')) {
  493. @mysqli_options($connection, MYSQLI_SET_CHARSET_NAME, $this->dsn['charset']);
  494. }
  495. if ($this->options['ssl']) {
  496. @mysqli_ssl_set(
  497. $connection,
  498. empty($this->dsn['key']) ? null : $this->dsn['key'],
  499. empty($this->dsn['cert']) ? null : $this->dsn['cert'],
  500. empty($this->dsn['ca']) ? null : $this->dsn['ca'],
  501. empty($this->dsn['capath']) ? null : $this->dsn['capath'],
  502. empty($this->dsn['cipher']) ? null : $this->dsn['cipher']
  503. );
  504. }
  505. if (!@mysqli_real_connect(
  506. $connection,
  507. $this->dsn['hostspec'],
  508. $username,
  509. $password,
  510. $this->database_name,
  511. $this->dsn['port'],
  512. $this->dsn['socket']
  513. )) {
  514. if (($err = @mysqli_connect_error()) != '') {
  515. return $this->raiseError(null,
  516. null, null, $err, __FUNCTION__);
  517. } else {
  518. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
  519. 'unable to establish a connection', __FUNCTION__);
  520. }
  521. }
  522. if (!empty($this->dsn['charset']) && !defined('MYSQLI_SET_CHARSET_NAME')) {
  523. $result = $this->setCharset($this->dsn['charset'], $connection);
  524. if (MDB2::isError($result)) {
  525. return $result;
  526. }
  527. }
  528. return $connection;
  529. }
  530. // }}}
  531. // {{{ connect()
  532. /**
  533. * Connect to the database
  534. *
  535. * @return true on success, MDB2 Error Object on failure
  536. */
  537. function connect()
  538. {
  539. if (is_object($this->connection)) {
  540. //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0) {
  541. if (MDB2::areEquals($this->connected_dsn, $this->dsn)) {
  542. return MDB2_OK;
  543. }
  544. $this->connection = 0;
  545. }
  546. $connection = $this->_doConnect(
  547. $this->dsn['username'],
  548. $this->dsn['password']
  549. );
  550. if (MDB2::isError($connection)) {
  551. return $connection;
  552. }
  553. $this->connection = $connection;
  554. $this->connected_dsn = $this->dsn;
  555. $this->connected_database_name = $this->database_name;
  556. $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
  557. $this->_getServerCapabilities();
  558. return MDB2_OK;
  559. }
  560. // }}}
  561. // {{{ setCharset()
  562. /**
  563. * Set the charset on the current connection
  564. *
  565. * @param string charset (or array(charset, collation))
  566. * @param resource connection handle
  567. *
  568. * @return true on success, MDB2 Error Object on failure
  569. */
  570. function setCharset($charset, $connection = null)
  571. {
  572. if (null === $connection) {
  573. $connection = $this->getConnection();
  574. if (MDB2::isError($connection)) {
  575. return $connection;
  576. }
  577. }
  578. $collation = null;
  579. if (is_array($charset) && 2 == count($charset)) {
  580. $collation = array_pop($charset);
  581. $charset = array_pop($charset);
  582. }
  583. $client_info = mysqli_get_client_version();
  584. if (OS_WINDOWS && ((40111 > $client_info) ||
  585. ((50000 <= $client_info) && (50006 > $client_info)))
  586. ) {
  587. $query = "SET NAMES '".mysqli_real_escape_string($connection, $charset)."'";
  588. if (null !== $collation) {
  589. $query .= " COLLATE '".mysqli_real_escape_string($connection, $collation)."'";
  590. }
  591. return $this->_doQuery($query, true, $connection);
  592. }
  593. if (!$result = mysqli_set_charset($connection, $charset)) {
  594. $err = $this->raiseError(null, null, null,
  595. 'Could not set client character set', __FUNCTION__);
  596. return $err;
  597. }
  598. return $result;
  599. }
  600. // }}}
  601. // {{{ databaseExists()
  602. /**
  603. * check if given database name is exists?
  604. *
  605. * @param string $name name of the database that should be checked
  606. *
  607. * @return mixed true/false on success, a MDB2 error on failure
  608. * @access public
  609. */
  610. function databaseExists($name)
  611. {
  612. $connection = $this->_doConnect($this->dsn['username'],
  613. $this->dsn['password']);
  614. if (MDB2::isError($connection)) {
  615. return $connection;
  616. }
  617. $result = @mysqli_select_db($connection, $name);
  618. @mysqli_close($connection);
  619. return $result;
  620. }
  621. // }}}
  622. // {{{ disconnect()
  623. /**
  624. * Log out and disconnect from the database.
  625. *
  626. * @param boolean $force if the disconnect should be forced even if the
  627. * connection is opened persistently
  628. * @return mixed true on success, false if not connected and error
  629. * object on error
  630. * @access public
  631. */
  632. function disconnect($force = true)
  633. {
  634. if (is_object($this->connection)) {
  635. if ($this->in_transaction) {
  636. $dsn = $this->dsn;
  637. $database_name = $this->database_name;
  638. $persistent = $this->options['persistent'];
  639. $this->dsn = $this->connected_dsn;
  640. $this->database_name = $this->connected_database_name;
  641. $this->options['persistent'] = $this->opened_persistent;
  642. $this->rollback();
  643. $this->dsn = $dsn;
  644. $this->database_name = $database_name;
  645. $this->options['persistent'] = $persistent;
  646. }
  647. if ($force) {
  648. $ok = @mysqli_close($this->connection);
  649. if (!$ok) {
  650. return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED,
  651. null, null, null, __FUNCTION__);
  652. }
  653. }
  654. } else {
  655. return false;
  656. }
  657. return parent::disconnect($force);
  658. }
  659. // }}}
  660. // {{{ standaloneQuery()
  661. /**
  662. * execute a query as DBA
  663. *
  664. * @param string $query the SQL query
  665. * @param mixed $types array that contains the types of the columns in
  666. * the result set
  667. * @param boolean $is_manip if the query is a manipulation query
  668. * @return mixed MDB2_OK on success, a MDB2 error on failure
  669. * @access public
  670. */
  671. function standaloneQuery($query, $types = null, $is_manip = false)
  672. {
  673. $user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username'];
  674. $pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password'];
  675. $connection = $this->_doConnect($user, $pass);
  676. if (MDB2::isError($connection)) {
  677. return $connection;
  678. }
  679. $offset = $this->offset;
  680. $limit = $this->limit;
  681. $this->offset = $this->limit = 0;
  682. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  683. $result = $this->_doQuery($query, $is_manip, $connection, $this->database_name);
  684. if (!MDB2::isError($result)) {
  685. $result = $this->_affectedRows($connection, $result);
  686. }
  687. @mysqli_close($connection);
  688. return $result;
  689. }
  690. // }}}
  691. // {{{ _doQuery()
  692. /**
  693. * Execute a query
  694. * @param string $query query
  695. * @param boolean $is_manip if the query is a manipulation query
  696. * @param resource $connection
  697. * @param string $database_name
  698. * @return result or error object
  699. * @access protected
  700. */
  701. function _doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  702. {
  703. $this->last_query = $query;
  704. $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  705. if ($result) {
  706. if (MDB2::isError($result)) {
  707. return $result;
  708. }
  709. $query = $result;
  710. }
  711. if ($this->options['disable_query']) {
  712. $result = $is_manip ? 0 : null;
  713. return $result;
  714. }
  715. if (null === $connection) {
  716. $connection = $this->getConnection();
  717. if (MDB2::isError($connection)) {
  718. return $connection;
  719. }
  720. }
  721. if (null === $database_name) {
  722. $database_name = $this->database_name;
  723. }
  724. if ($database_name) {
  725. if ($database_name != $this->connected_database_name) {
  726. if (!@mysqli_select_db($connection, $database_name)) {
  727. $err = $this->raiseError(null, null, null,
  728. 'Could not select the database: '.$database_name, __FUNCTION__);
  729. return $err;
  730. }
  731. $this->connected_database_name = $database_name;
  732. }
  733. }
  734. if ($this->options['multi_query']) {
  735. $result = mysqli_multi_query($connection, $query);
  736. } else {
  737. $resultmode = $this->options['result_buffering'] ? MYSQLI_USE_RESULT : MYSQLI_USE_RESULT;
  738. $result = mysqli_query($connection, $query);
  739. }
  740. if (!$result) {
  741. // Store now because standaloneQuery throws off $this->connection.
  742. $this->_query_errno = mysqli_errno($connection);
  743. if (0 !== $this->_query_errno) {
  744. $this->_query_error = mysqli_error($connection);
  745. $err = $this->raiseError(null, null, null,
  746. 'Could not execute statement', __FUNCTION__);
  747. return $err;
  748. }
  749. }
  750. if ($this->options['multi_query']) {
  751. if ($this->options['result_buffering']) {
  752. if (!($result = @mysqli_store_result($connection))) {
  753. $err = $this->raiseError(null, null, null,
  754. 'Could not get the first result from a multi query', __FUNCTION__);
  755. return $err;
  756. }
  757. } elseif (!($result = @mysqli_use_result($connection))) {
  758. $err = $this->raiseError(null, null, null,
  759. 'Could not get the first result from a multi query', __FUNCTION__);
  760. return $err;
  761. }
  762. }
  763. $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
  764. return $result;
  765. }
  766. // }}}
  767. // {{{ _affectedRows()
  768. /**
  769. * Returns the number of rows affected
  770. *
  771. * @param resource $result
  772. * @param resource $connection
  773. * @return mixed MDB2 Error Object or the number of rows affected
  774. * @access private
  775. */
  776. function _affectedRows($connection, $result = null)
  777. {
  778. if (null === $connection) {
  779. $connection = $this->getConnection();
  780. if (MDB2::isError($connection)) {
  781. return $connection;
  782. }
  783. }
  784. return @mysqli_affected_rows($connection);
  785. }
  786. // }}}
  787. // {{{ _modifyQuery()
  788. /**
  789. * Changes a query string for various DBMS specific reasons
  790. *
  791. * @param string $query query to modify
  792. * @param boolean $is_manip if it is a DML query
  793. * @param integer $limit limit the number of rows
  794. * @param integer $offset start reading from given offset
  795. * @return string modified query
  796. * @access protected
  797. */
  798. function _modifyQuery($query, $is_manip, $limit, $offset)
  799. {
  800. if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) {
  801. // "DELETE FROM table" gives 0 affected rows in MySQL.
  802. // This little hack lets you know how many rows were deleted.
  803. if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
  804. $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
  805. 'DELETE FROM \1 WHERE 1=1', $query);
  806. }
  807. }
  808. if ($limit > 0
  809. && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query)
  810. ) {
  811. $query = rtrim($query);
  812. if (substr($query, -1) == ';') {
  813. $query = substr($query, 0, -1);
  814. }
  815. // LIMIT doesn't always come last in the query
  816. // @see http://dev.mysql.com/doc/refman/5.0/en/select.html
  817. $after = '';
  818. if (preg_match('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', $query, $matches)) {
  819. $after = $matches[0];
  820. $query = preg_replace('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', '', $query);
  821. } elseif (preg_match('/(\s+FOR\s+UPDATE\s*)$/i', $query, $matches)) {
  822. $after = $matches[0];
  823. $query = preg_replace('/(\s+FOR\s+UPDATE\s*)$/im', '', $query);
  824. } elseif (preg_match('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', $query, $matches)) {
  825. $after = $matches[0];
  826. $query = preg_replace('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', '', $query);
  827. }
  828. if ($is_manip) {
  829. return $query . " LIMIT $limit" . $after;
  830. } else {
  831. return $query . " LIMIT $offset, $limit" . $after;
  832. }
  833. }
  834. return $query;
  835. }
  836. // }}}
  837. // {{{ getServerVersion()
  838. /**
  839. * return version information about the server
  840. *
  841. * @param bool $native determines if the raw version string should be returned
  842. * @return mixed array/string with version information or MDB2 error object
  843. * @access public
  844. */
  845. function getServerVersion($native = false)
  846. {
  847. $connection = $this->getConnection();
  848. if (MDB2::isError($connection)) {
  849. return $connection;
  850. }
  851. if ($this->connected_server_info) {
  852. $server_info = $this->connected_server_info;
  853. } else {
  854. $server_info = @mysqli_get_server_info($connection);
  855. }
  856. if (!$server_info) {
  857. return $this->raiseError(null, null, null,
  858. 'Could not get server information', __FUNCTION__);
  859. }
  860. // cache server_info
  861. $this->connected_server_info = $server_info;
  862. if (!$native) {
  863. $tmp = explode('.', $server_info, 3);
  864. if (isset($tmp[2]) && strpos($tmp[2], '-')) {
  865. $tmp2 = explode('-', @$tmp[2], 2);
  866. } else {
  867. $tmp2[0] = isset($tmp[2]) ? $tmp[2] : null;
  868. $tmp2[1] = null;
  869. }
  870. $server_info = array(
  871. 'major' => isset($tmp[0]) ? $tmp[0] : null,
  872. 'minor' => isset($tmp[1]) ? $tmp[1] : null,
  873. 'patch' => $tmp2[0],
  874. 'extra' => $tmp2[1],
  875. 'native' => $server_info,
  876. );
  877. }
  878. return $server_info;
  879. }
  880. // }}}
  881. // {{{ _getServerCapabilities()
  882. /**
  883. * Fetch some information about the server capabilities
  884. * (transactions, subselects, prepared statements, etc).
  885. *
  886. * @access private
  887. */
  888. function _getServerCapabilities()
  889. {
  890. if (!$this->server_capabilities_checked) {
  891. $this->server_capabilities_checked = true;
  892. //set defaults
  893. $this->supported['sub_selects'] = 'emulated';
  894. $this->supported['prepared_statements'] = 'emulated';
  895. $this->supported['triggers'] = false;
  896. $this->start_transaction = false;
  897. $this->varchar_max_length = 255;
  898. $server_info = $this->getServerVersion();
  899. if (is_array($server_info)) {
  900. $server_version = $server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'];
  901. if (!version_compare($server_version, '4.1.0', '<')) {
  902. $this->supported['sub_selects'] = true;
  903. $this->supported['prepared_statements'] = true;
  904. }
  905. // SAVEPOINTs were introduced in MySQL 4.0.14 and 4.1.1 (InnoDB)
  906. if (version_compare($server_version, '4.1.0', '>=')) {
  907. if (version_compare($server_version, '4.1.1', '<')) {
  908. $this->supported['savepoints'] = false;
  909. }
  910. } elseif (version_compare($server_version, '4.0.14', '<')) {
  911. $this->supported['savepoints'] = false;
  912. }
  913. if (!version_compare($server_version, '4.0.11', '<')) {
  914. $this->start_transaction = true;
  915. }
  916. if (!version_compare($server_version, '5.0.3', '<')) {
  917. $this->varchar_max_length = 65532;
  918. }
  919. if (!version_compare($server_version, '5.0.2', '<')) {
  920. $this->supported['triggers'] = true;
  921. }
  922. }
  923. }
  924. }
  925. // }}}
  926. // {{{ function _skipUserDefinedVariable($query, $position)
  927. /**
  928. * Utility method, used by prepare() to avoid misinterpreting MySQL user
  929. * defined variables (SELECT @x:=5) for placeholders.
  930. * Check if the placeholder is a false positive, i.e. if it is an user defined
  931. * variable instead. If so, skip it and advance the position, otherwise
  932. * return the current position, which is valid
  933. *
  934. * @param string $query
  935. * @param integer $position current string cursor position
  936. * @return integer $new_position
  937. * @access protected
  938. */
  939. function _skipUserDefinedVariable($query, $position)
  940. {
  941. $found = strpos(strrev(substr($query, 0, $position)), '@');
  942. if (false === $found) {
  943. return $position;
  944. }
  945. $pos = strlen($query) - strlen(substr($query, $position)) - $found - 1;
  946. $substring = substr($query, $pos, $position - $pos + 2);
  947. if (preg_match('/^@\w+\s*:=$/', $substring)) {
  948. return $position + 1; //found an user defined variable: skip it
  949. }
  950. return $position;
  951. }
  952. // }}}
  953. // {{{ prepare()
  954. /**
  955. * Prepares a query for multiple execution with execute().
  956. * With some database backends, this is emulated.
  957. * prepare() requires a generic query as string like
  958. * 'INSERT INTO numbers VALUES(?,?)' or
  959. * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  960. * The ? and :name and are placeholders which can be set using
  961. * bindParam() and the query can be sent off using the execute() method.
  962. * The allowed format for :name can be set with the 'bindname_format' option.
  963. *
  964. * @param string $query the query to prepare
  965. * @param mixed $types array that contains the types of the placeholders
  966. * @param mixed $result_types array that contains the types of the columns in
  967. * the result set or MDB2_PREPARE_RESULT, if set to
  968. * MDB2_PREPARE_MANIP the query is handled as a manipulation query
  969. * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders
  970. * @return mixed resource handle for the prepared query on success, a MDB2
  971. * error on failure
  972. * @access public
  973. * @see bindParam, execute
  974. */
  975. function prepare($query, $types = null, $result_types = null, $lobs = array())
  976. {
  977. // connect to get server capabilities (http://pear.php.net/bugs/16147)
  978. $connection = $this->getConnection();
  979. if (MDB2::isError($connection)) {
  980. return $connection;
  981. }
  982. if ($this->options['emulate_prepared']
  983. || $this->supported['prepared_statements'] !== true
  984. ) {
  985. return parent::prepare($query, $types, $result_types, $lobs);
  986. }
  987. $is_manip = ($result_types === MDB2_PREPARE_MANIP);
  988. $offset = $this->offset;
  989. $limit = $this->limit;
  990. $this->offset = $this->limit = 0;
  991. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  992. $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
  993. if ($result) {
  994. if (MDB2::isError($result)) {
  995. return $result;
  996. }
  997. $query = $result;
  998. }
  999. $placeholder_type_guess = $placeholder_type = null;
  1000. $question = '?';
  1001. $colon = ':';
  1002. $positions = array();
  1003. $position = 0;
  1004. while ($position < strlen($query)) {
  1005. $q_position = strpos($query, $question, $position);
  1006. $c_position = strpos($query, $colon, $position);
  1007. if ($q_position && $c_position) {
  1008. $p_position = min($q_position, $c_position);
  1009. } elseif ($q_position) {
  1010. $p_position = $q_position;
  1011. } elseif ($c_position) {
  1012. $p_position = $c_position;
  1013. } else {
  1014. break;
  1015. }
  1016. if (null === $placeholder_type) {
  1017. $placeholder_type_guess = $query[$p_position];
  1018. }
  1019. $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
  1020. if (MDB2::isError($new_pos)) {
  1021. return $new_pos;
  1022. }
  1023. if ($new_pos != $position) {
  1024. $position = $new_pos;
  1025. continue; //evaluate again starting from the new position
  1026. }
  1027. //make sure this is not part of an user defined variable
  1028. $new_pos = $this->_skipUserDefinedVariable($query, $position);
  1029. if ($new_pos != $position) {
  1030. $position = $new_pos;
  1031. continue; //evaluate again starting from the new position
  1032. }
  1033. if ($query[$position] == $placeholder_type_guess) {
  1034. if (null === $placeholder_type) {
  1035. $placeholder_type = $query[$p_position];
  1036. $question = $colon = $placeholder_type;
  1037. }
  1038. if ($placeholder_type == ':') {
  1039. $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s';
  1040. $parameter = preg_replace($regexp, '\\1', $query);
  1041. if ($parameter === '') {
  1042. $err = $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  1043. 'named parameter name must match "bindname_format" option', __FUNCTION__);
  1044. return $err;
  1045. }
  1046. $positions[$p_position] = $parameter;
  1047. $query = substr_replace($query, '?', $position, strlen($parameter)+1);
  1048. } else {
  1049. $positions[$p_position] = count($positions);
  1050. }
  1051. $position = $p_position + 1;
  1052. } else {
  1053. $position = $p_position;
  1054. }
  1055. }
  1056. if (!$is_manip) {
  1057. static $prep_statement_counter = 1;
  1058. $randomStr = sprintf('%d', $prep_statement_counter++) . sha1(microtime() . sprintf('%d', mt_rand()));
  1059. $statement_name = sprintf($this->options['statement_format'], $this->phptype, $randomStr);
  1060. $statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']);
  1061. $query = "PREPARE $statement_name FROM ".$this->quote($query, 'text');
  1062. $statement = $this->_doQuery($query, true, $connection);
  1063. if (MDB2::isError($statement)) {
  1064. return $statement;
  1065. }
  1066. $statement = $statement_name;
  1067. } else {
  1068. $statement = @mysqli_prepare($connection, $query);
  1069. if (!$statement) {
  1070. $err = $this->raiseError(null, null, null,
  1071. 'Unable to create prepared statement handle', __FUNCTION__);
  1072. return $err;
  1073. }
  1074. }
  1075. $class_name = 'MDB2_Statement_'.$this->phptype;
  1076. $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  1077. $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
  1078. return $obj;
  1079. }
  1080. // }}}
  1081. // {{{ replace()
  1082. /**
  1083. * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
  1084. * query, except that if there is already a row in the table with the same
  1085. * key field values, the old row is deleted before the new row is inserted.
  1086. *
  1087. * The REPLACE type of query does not make part of the SQL standards. Since
  1088. * practically only MySQL implements it natively, this type of query is
  1089. * emulated through this method for other DBMS using standard types of
  1090. * queries inside a transaction to assure the atomicity of the operation.
  1091. *
  1092. * @access public
  1093. *
  1094. * @param string $table name of the table on which the REPLACE query will
  1095. * be executed.
  1096. * @param array $fields associative array that describes the fields and the
  1097. * values that will be inserted or updated in the specified table. The
  1098. * indexes of the array are the names of all the fields of the table. The
  1099. * values of the array are also associative arrays that describe the
  1100. * values and other properties of the table fields.
  1101. *
  1102. * Here follows a list of field properties that need to be specified:
  1103. *
  1104. * value:
  1105. * Value to be assigned to the specified field. This value may be
  1106. * of specified in database independent type format as this
  1107. * function can perform the necessary datatype conversions.
  1108. *
  1109. * Default:
  1110. * this property is required unless the Null property
  1111. * is set to 1.
  1112. *
  1113. * type
  1114. * Name of the type of the field. Currently, all types Metabase
  1115. * are supported except for clob and blob.
  1116. *
  1117. * Default: no type conversion
  1118. *
  1119. * null
  1120. * Boolean property that indicates that the value for this field
  1121. * should be set to null.
  1122. *
  1123. * The default value for fields missing in INSERT queries may be
  1124. * specified the definition of a table. Often, the default value
  1125. * is already null, but since the REPLACE may be emulated using
  1126. * an UPDATE query, make sure that all fields of the table are
  1127. * listed in this function argument array.
  1128. *
  1129. * Default: 0
  1130. *
  1131. * key
  1132. * Boolean property that indicates that this field should be
  1133. * handled as a primary key or at least as part of the compound
  1134. * unique index of the table that will determine the row that will
  1135. * updated if it exists or inserted a new row otherwise.
  1136. *
  1137. * This function will fail if no key field is specified or if the
  1138. * value of a key field is set to null because fields that are
  1139. * part of unique index they may not be null.
  1140. *
  1141. * Default: 0
  1142. *
  1143. * @see http://dev.mysql.com/doc/refman/5.0/en/replace.html
  1144. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1145. */
  1146. function replace($table, $fields)
  1147. {
  1148. $count = count($fields);
  1149. $query = $values = '';
  1150. $keys = $colnum = 0;
  1151. for (reset($fields); $colnum < $count; next($fields), $colnum++) {
  1152. $name = key($fields);
  1153. if ($colnum > 0) {
  1154. $query .= ',';
  1155. $values.= ',';
  1156. }
  1157. $query.= $this->quoteIdentifier($name, true);
  1158. if (isset($fields[$name]['null']) && $fields[$name]['null']) {
  1159. $value = 'NULL';
  1160. } else {
  1161. $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
  1162. $value = $this->quote($fields[$name]['value'], $type);
  1163. if (MDB2::isError($value)) {
  1164. return $value;
  1165. }
  1166. }
  1167. $values.= $value;
  1168. if (isset($fields[$name]['key']) && $fields[$name]['key']) {
  1169. if ($value === 'NULL') {
  1170. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  1171. 'key value '.$name.' may not be NULL', __FUNCTION__);
  1172. }
  1173. $keys++;
  1174. }
  1175. }
  1176. if ($keys == 0) {
  1177. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  1178. 'not specified which fields are keys', __FUNCTION__);
  1179. }
  1180. $connection = $this->getConnection();
  1181. if (MDB2::isError($connection)) {
  1182. return $connection;
  1183. }
  1184. $table = $this->quoteIdentifier($table, true);
  1185. $query = "REPLACE INTO $table ($query) VALUES ($values)";
  1186. $result = $this->_doQuery($query, true, $connection);
  1187. if (MDB2::isError($result)) {
  1188. return $result;
  1189. }
  1190. return $this->_affectedRows($connection, $result);
  1191. }
  1192. // }}}
  1193. // {{{ nextID()
  1194. /**
  1195. * Returns the next free id of a sequence
  1196. *
  1197. * @param string $seq_name name of the sequence
  1198. * @param boolean $ondemand when true the sequence is
  1199. * automatic created, if it
  1200. * not exists
  1201. *
  1202. * @return mixed MDB2 Error Object or id
  1203. * @access public
  1204. */
  1205. function nextID($seq_name, $ondemand = true)
  1206. {
  1207. $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
  1208. $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
  1209. $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)";
  1210. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  1211. $this->expectError(MDB2_ERROR_NOSUCHTABLE);
  1212. $result = $this->_doQuery($query, true);
  1213. $this->popExpect();
  1214. $this->popErrorHandling();
  1215. if (MDB2::isError($result)) {
  1216. if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
  1217. $this->loadModule('Manager', null, true);
  1218. $result = $this->manager->createSequence($seq_name);
  1219. if (MDB2::isError($result)) {
  1220. return $this->raiseError($result, null, null,
  1221. 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
  1222. } else {
  1223. return $this->nextID($seq_name, false);
  1224. }
  1225. }
  1226. return $result;
  1227. }
  1228. $value = $this->lastInsertID();
  1229. if (is_numeric($value)) {
  1230. $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
  1231. $result = $this->_doQuery($query, true);
  1232. if (MDB2::isError($result)) {
  1233. $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
  1234. }
  1235. }
  1236. return $value;
  1237. }
  1238. // }}}
  1239. // {{{ lastInsertID()
  1240. /**
  1241. * Returns the autoincrement ID if supported or $id or fetches the current
  1242. * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  1243. *
  1244. * @param string $table name of the table into which a new row was inserted
  1245. * @param string $field name of the field into which a new row was inserted
  1246. * @return mixed MDB2 Error Object or id
  1247. * @access public
  1248. */
  1249. function lastInsertID($table = null, $field = null)
  1250. {
  1251. // not using mysql_insert_id() due to http://pear.php.net/bugs/bug.php?id=8051
  1252. // not casting to integer to handle BIGINT http://pear.php.net/bugs/bug.php?id=17650
  1253. return $this->queryOne('SELECT LAST_INSERT_ID()');
  1254. }
  1255. // }}}
  1256. // {{{ currID()
  1257. /**
  1258. * Returns the current id of a sequence
  1259. *
  1260. * @param string $seq_name name of the sequence
  1261. * @return mixed MDB2 Error Object or id
  1262. * @access public
  1263. */
  1264. function currID($seq_name)
  1265. {
  1266. $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
  1267. $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
  1268. $query = "SELECT MAX($seqcol_name) FROM $sequence_name";
  1269. return $this->queryOne($query, 'integer');
  1270. }
  1271. }
  1272. /**
  1273. * MDB2 MySQLi result driver
  1274. *
  1275. * @package MDB2
  1276. * @category Database
  1277. * @author Lukas Smith <smith@pooteeweet.org>
  1278. */
  1279. class MDB2_Result_mysqli extends MDB2_Result_Common
  1280. {
  1281. // }}}
  1282. // {{{ fetchRow()
  1283. /**
  1284. * Fetch a row and insert the data into an existing array.
  1285. *
  1286. * @param int $fetchmode how the array data should be indexed
  1287. * @param int $rownum number of the row where the data can be found
  1288. * @return int data array on success, a MDB2 error on failure
  1289. * @access public
  1290. */
  1291. function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  1292. {
  1293. if (null !== $rownum) {
  1294. $seek = $this->seek($rownum);
  1295. if (MDB2::isError($seek)) {
  1296. return $seek;
  1297. }
  1298. }
  1299. if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
  1300. $fetchmode = $this->db->fetchmode;
  1301. }
  1302. if ( $fetchmode == MDB2_FETCHMODE_ASSOC
  1303. || $fetchmode == MDB2_FETCHMODE_OBJECT
  1304. ) {
  1305. $row = @mysqli_fetch_assoc($this->result);
  1306. if (is_array($row)
  1307. && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
  1308. ) {
  1309. $row = array_change_key_case($row, $this->db->options['field_case']);
  1310. }
  1311. } else {
  1312. $row = @mysqli_fetch_row($this->result);
  1313. }
  1314. if (!$row) {
  1315. if (false === $this->result) {
  1316. $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  1317. 'resultset has already been freed', __FUNCTION__);
  1318. return $err;
  1319. }
  1320. return null;
  1321. }
  1322. $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
  1323. $rtrim = false;
  1324. if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
  1325. if (empty($this->types)) {
  1326. $mode += MDB2_PORTABILITY_RTRIM;
  1327. } else {
  1328. $rtrim = true;
  1329. }
  1330. }
  1331. if ($mode) {
  1332. $this->db->_fixResultArrayValues($row, $mode);
  1333. }
  1334. if ( ( $fetchmode != MDB2_FETCHMODE_ASSOC
  1335. && $fetchmode != MDB2_FETCHMODE_OBJECT)
  1336. && !empty($this->types)
  1337. ) {
  1338. $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
  1339. } elseif (($fetchmode == MDB2_FETCHMODE_ASSOC
  1340. || $fetchmode == MDB2_FETCHMODE_OBJECT)
  1341. && !empty($this->types_assoc)
  1342. ) {
  1343. $row = $this->db->datatype->convertResultRow($this->types_assoc, $row, $rtrim);
  1344. }
  1345. if (!empty($this->values)) {
  1346. $this->_assignBindColumns($row);
  1347. }
  1348. if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
  1349. $object_class = $this->db->options['fetch_class'];
  1350. if ($object_class == 'stdClass') {
  1351. $row = (object) $row;
  1352. } else {
  1353. $rowObj = new $object_class($row);
  1354. $row = $rowObj;
  1355. }
  1356. }
  1357. ++$this->rownum;
  1358. return $row;
  1359. }
  1360. // }}}
  1361. // {{{ _getColumnNames()
  1362. /**
  1363. * Retrieve the names of columns returned by the DBMS in a query result.
  1364. *
  1365. * @return mixed Array variable that holds the names of columns as keys
  1366. * or an MDB2 error on failure.
  1367. * Some DBMS may not return any columns when the result set
  1368. * does not contain any rows.
  1369. * @access private
  1370. */
  1371. function _getColumnNames()
  1372. {
  1373. $columns = array();
  1374. $numcols = $this->numCols();
  1375. if (MDB2::isError($numcols)) {
  1376. return $numcols;
  1377. }
  1378. for ($column = 0; $column < $numcols; $column++) {
  1379. $column_info = @mysqli_fetch_field_direct($this->result, $column);
  1380. $columns[$column_info->name] = $column;
  1381. }
  1382. if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  1383. $columns = array_change_key_case($columns, $this->db->options['field_case']);
  1384. }
  1385. return $columns;
  1386. }
  1387. // }}}
  1388. // {{{ numCols()
  1389. /**
  1390. * Count the number of columns returned by the DBMS in a query result.
  1391. *
  1392. * @return mixed integer value with the number of columns, a MDB2 error
  1393. * on failure
  1394. * @access public
  1395. */
  1396. function numCols()
  1397. {
  1398. $cols = @mysqli_num_fields($this->result);
  1399. if (null === $cols) {
  1400. if (false === $this->result) {
  1401. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  1402. 'resultset has already been freed', __FUNCTION__);
  1403. }
  1404. if (null === $this->result) {
  1405. return count($this->types);
  1406. }
  1407. return $this->db->raiseError(null, null, null,
  1408. 'Could not get column count', __FUNCTION__);
  1409. }
  1410. return $cols;
  1411. }
  1412. // }}}
  1413. // {{{ nextResult()
  1414. /**
  1415. * Move the internal result pointer to the next available result
  1416. *
  1417. * @return true on success, false if there is no more result set or an error object on failure
  1418. * @access public
  1419. */
  1420. function nextResult()
  1421. {
  1422. $connection = $this->db->getConnection();
  1423. if (MDB2::isError($connection)) {
  1424. return $connection;
  1425. }
  1426. if (!@mysqli_more_results($connection)) {
  1427. return false;
  1428. }
  1429. if (!@mysqli_next_result($connection)) {
  1430. return false;
  1431. }
  1432. if (!($this->result = @mysqli_use_result($connection))) {
  1433. return false;
  1434. }
  1435. return MDB2_OK;
  1436. }
  1437. // }}}
  1438. // {{{ free()
  1439. /**
  1440. * Free the internal resources associated with result.
  1441. *
  1442. * @return boolean true on success, false if result is invalid
  1443. * @access public
  1444. */
  1445. function free()
  1446. {
  1447. do {
  1448. if (is_object($this->result) && $this->db->connection) {
  1449. $free = @mysqli_free_result($this->result);
  1450. if (false === $free) {
  1451. return $this->db->raiseError(null, null, null,
  1452. 'Could not free result', __FUNCTION__);
  1453. }
  1454. }
  1455. } while ($this->result = $this->nextResult());
  1456. $this->result = false;
  1457. return MDB2_OK;
  1458. }
  1459. }
  1460. /**
  1461. * MDB2 MySQLi buffered result driver
  1462. *
  1463. * @package MDB2
  1464. * @category Database
  1465. * @author Lukas Smith <smith@pooteeweet.org>
  1466. */
  1467. class MDB2_BufferedResult_mysqli extends MDB2_Result_mysqli
  1468. {
  1469. // }}}
  1470. // {{{ seek()
  1471. /**
  1472. * Seek to a specific row in a result set
  1473. *
  1474. * @param int $rownum number of the row where the data can be found
  1475. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1476. * @access public
  1477. */
  1478. function seek($rownum = 0)
  1479. {
  1480. if ($this->rownum != ($rownum - 1) && !@mysqli_data_seek($this->result, $rownum)) {
  1481. if (false === $this->result) {
  1482. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  1483. 'resultset has already been freed', __FUNCTION__);
  1484. }
  1485. if (null === $this->result) {
  1486. return MDB2_OK;
  1487. }
  1488. return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
  1489. 'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
  1490. }
  1491. $this->rownum = $rownum - 1;
  1492. return MDB2_OK;
  1493. }
  1494. // }}}
  1495. // {{{ valid()
  1496. /**
  1497. * Check if the end of the result set has been reached
  1498. *
  1499. * @return mixed true or false on sucess, a MDB2 error on failure
  1500. * @access public
  1501. */
  1502. function valid()
  1503. {
  1504. $numrows = $this->numRows();
  1505. if (MDB2::isError($numrows)) {
  1506. return $numrows;
  1507. }
  1508. return $this->rownum < ($numrows - 1);
  1509. }
  1510. // }}}
  1511. // {{{ numRows()
  1512. /**
  1513. * Returns the number of rows in a result object
  1514. *
  1515. * @return mixed MDB2 Error Object or the number of rows
  1516. * @access public
  1517. */
  1518. function numRows()
  1519. {
  1520. $rows = @mysqli_num_rows($this->result);
  1521. if (null === $rows) {
  1522. if (false === $this->result) {
  1523. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  1524. 'resultset has already been freed', __FUNCTION__);
  1525. }
  1526. if (null === $this->result) {
  1527. return 0;
  1528. }
  1529. return $this->db->raiseError(null, null, null,
  1530. 'Could not get row count', __FUNCTION__);
  1531. }
  1532. return $rows;
  1533. }
  1534. // }}}
  1535. // {{{ nextResult()
  1536. /**
  1537. * Move the internal result pointer to the next available result
  1538. *
  1539. * @param a valid result resource
  1540. * @return true on success, false if there is no more result set or an error object on failure
  1541. * @access public
  1542. */
  1543. function nextResult()
  1544. {
  1545. $connection = $this->db->getConnection();
  1546. if (MDB2::isError($connection)) {
  1547. return $connection;
  1548. }
  1549. if (!@mysqli_more_results($connection)) {
  1550. return false;
  1551. }
  1552. if (!@mysqli_next_result($connection)) {
  1553. return false;
  1554. }
  1555. if (!($this->result = @mysqli_store_result($connection))) {
  1556. return false;
  1557. }
  1558. return MDB2_OK;
  1559. }
  1560. }
  1561. /**
  1562. * MDB2 MySQLi statement driver
  1563. *
  1564. * @package MDB2
  1565. * @category Database
  1566. * @author Lukas Smith <smith@pooteeweet.org>
  1567. */
  1568. class MDB2_Statement_mysqli extends MDB2_Statement_Common
  1569. {
  1570. // {{{ _execute()
  1571. /**
  1572. * Execute a prepared query statement helper method.
  1573. *
  1574. * @param mixed $result_class string which specifies which result class to use
  1575. * @param mixed $result_wrap_class string which specifies which class to wrap results in
  1576. *
  1577. * @return mixed MDB2_Result or integer (affected rows) on success,
  1578. * a MDB2 error on failure
  1579. * @access private
  1580. */
  1581. function _execute($result_class = true, $result_wrap_class = true)
  1582. {
  1583. if (null === $this->statement) {
  1584. $result = parent::_execute($result_class, $result_wrap_class);
  1585. return $result;
  1586. }
  1587. $this->db->last_query = $this->query;
  1588. $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values));
  1589. if ($this->db->getOption('disable_query')) {
  1590. $result = $this->is_manip ? 0 : null;
  1591. return $result;
  1592. }
  1593. $connection = $this->db->getConnection();
  1594. if (MDB2::isError($connection)) {
  1595. return $connection;
  1596. }
  1597. if (!is_object($this->statement)) {
  1598. $query = 'EXECUTE '.$this->statement;
  1599. }
  1600. if (!empty($this->positions)) {
  1601. $paramReferences = array();
  1602. $parameters = array(0 => $this->statement, 1 => '');
  1603. $lobs = array();
  1604. $i = 0;
  1605. foreach ($this->positions as $parameter) {
  1606. if (!array_key_exists($parameter, $this->values)) {
  1607. return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  1608. 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
  1609. }
  1610. $value = $this->values[$parameter];
  1611. $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
  1612. if (!is_object($this->statement)) {
  1613. if (is_resource($value) || $type == 'clob' || $type == 'blob' && $this->db->options['lob_allow_url_include']) {
  1614. if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
  1615. if ($match[1] == 'file://') {
  1616. $value = $match[2];
  1617. }
  1618. $value = @fopen($value, 'r');
  1619. $close = true;
  1620. }
  1621. if (is_resource($value)) {
  1622. $data = '';
  1623. while (!@feof($value)) {
  1624. $data.= @fread($value, $this->db->options['lob_buffer_length']);
  1625. }
  1626. if ($close) {
  1627. @fclose($value);
  1628. }
  1629. $value = $data;
  1630. }
  1631. }
  1632. $quoted = $this->db->quote($value, $type);
  1633. if (MDB2::isError($quoted)) {
  1634. return $quoted;
  1635. }
  1636. $param_query = 'SET @'.$parameter.' = '.$quoted;
  1637. $result = $this->db->_doQuery($param_query, true, $connection);
  1638. if (MDB2::isError($result)) {
  1639. return $result;
  1640. }
  1641. } else {
  1642. if (is_resource($value) || $type == 'clob' || $type == 'blob') {
  1643. $paramReferences[$i] = null;
  1644. // mysqli_stmt_bind_param() requires parameters to be passed by reference
  1645. $parameters[] =& $paramReferences[$i];
  1646. $parameters[1].= 'b';
  1647. $lobs[$i] = $parameter;
  1648. } else {
  1649. $paramReferences[$i] = $this->db->quote($value, $type, false);
  1650. if (MDB2::isError($paramReferences[$i])) {
  1651. return $paramReferences[$i];
  1652. }
  1653. // mysqli_stmt_bind_param() requires parameters to be passed by reference
  1654. $parameters[] =& $paramReferences[$i];
  1655. $parameters[1].= $this->db->datatype->mapPrepareDatatype($type);
  1656. }
  1657. ++$i;
  1658. }
  1659. }
  1660. if (!is_object($this->statement)) {
  1661. $query.= ' USING @'.implode(', @', array_values($this->positions));
  1662. } else {
  1663. $result = call_user_func_array('mysqli_stmt_bind_param', $parameters);
  1664. if (false === $result) {
  1665. $err = $this->db->raiseError(null, null, null,
  1666. 'Unable to bind parameters', __FUNCTION__);
  1667. return $err;
  1668. }
  1669. foreach ($lobs as $i => $parameter) {
  1670. $value = $this->values[$parameter];
  1671. $close = false;
  1672. if (!is_resource($value)) {
  1673. $close = true;
  1674. if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
  1675. if ($match[1] == 'file://') {
  1676. $value = $match[2];
  1677. }
  1678. $value = @fopen($value, 'r');
  1679. } else {
  1680. $fp = @tmpfile();
  1681. @fwrite($fp, $value);
  1682. @rewind($fp);
  1683. $value = $fp;
  1684. }
  1685. }
  1686. while (!@feof($value)) {
  1687. $data = @fread($value, $this->db->options['lob_buffer_length']);
  1688. @mysqli_stmt_send_long_data($this->statement, $i, $data);
  1689. }
  1690. if ($close) {
  1691. @fclose($value);
  1692. }
  1693. }
  1694. }
  1695. }
  1696. if (!is_object($this->statement)) {
  1697. $result = $this->db->_doQuery($query, $this->is_manip, $connection);
  1698. if (MDB2::isError($result)) {
  1699. return $result;
  1700. }
  1701. if ($this->is_manip) {
  1702. $affected_rows = $this->db->_affectedRows($connection, $result);
  1703. return $affected_rows;
  1704. }
  1705. $result = $this->db->_wrapResult($result, $this->result_types,
  1706. $result_class, $result_wrap_class, $this->limit, $this->offset);
  1707. } else {
  1708. if (!mysqli_stmt_execute($this->statement)) {
  1709. $err = $this->db->raiseError(null, null, null,
  1710. 'Unable to execute statement', __FUNCTION__);
  1711. return $err;
  1712. }
  1713. if ($this->is_manip) {
  1714. $affected_rows = @mysqli_stmt_affected_rows($this->statement);
  1715. return $affected_rows;
  1716. }
  1717. if ($this->db->options['result_buffering']) {
  1718. @mysqli_stmt_store_result($this->statement);
  1719. }
  1720. $result = $this->db->_wrapResult($this->statement, $this->result_types,
  1721. $result_class, $result_wrap_class, $this->limit, $this->offset);
  1722. }
  1723. $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
  1724. return $result;
  1725. }
  1726. // }}}
  1727. // {{{ free()
  1728. /**
  1729. * Release resources allocated for the specified prepared query.
  1730. *
  1731. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1732. * @access public
  1733. */
  1734. function free()
  1735. {
  1736. if (null === $this->positions) {
  1737. return $this->db->raiseError(MDB2_ERROR, null, null,
  1738. 'Prepared statement has already been freed', __FUNCTION__);
  1739. }
  1740. $result = MDB2_OK;
  1741. if (is_object($this->statement)) {
  1742. if (!@mysqli_stmt_close($this->statement)) {
  1743. $result = $this->db->raiseError(null, null, null,
  1744. 'Could not free statement', __FUNCTION__);
  1745. }
  1746. } elseif (null !== $this->statement) {
  1747. $connection = $this->db->getConnection();
  1748. if (MDB2::isError($connection)) {
  1749. return $connection;
  1750. }
  1751. $query = 'DEALLOCATE PREPARE '.$this->statement;
  1752. $result = $this->db->_doQuery($query, true, $connection);
  1753. }
  1754. parent::free();
  1755. return $result;
  1756. }
  1757. }
  1758. ?>