PageRenderTime 67ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/common/libraries/plugin/pear/MDB2.php

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 4734 lines | 2095 code | 530 blank | 2109 comment | 293 complexity | 1ee6e7244bd4a5801ab352545f868f0f MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT, GPL-2.0
  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2007 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: MDB2.php 137 2009-11-09 13:24:37Z vanpouckesven $
  47. //
  48. /**
  49. * @package MDB2
  50. * @category Database
  51. * @author Lukas Smith <smith@pooteeweet.org>
  52. */
  53. require_once 'PEAR.php';
  54. // {{{ Error constants
  55. /**
  56. * The method mapErrorCode in each MDB2_dbtype implementation maps
  57. * native error codes to one of these.
  58. *
  59. * If you add an error code here, make sure you also add a textual
  60. * version of it in MDB2::errorMessage().
  61. */
  62. define('MDB2_OK', true);
  63. define('MDB2_ERROR', - 1);
  64. define('MDB2_ERROR_SYNTAX', - 2);
  65. define('MDB2_ERROR_CONSTRAINT', - 3);
  66. define('MDB2_ERROR_NOT_FOUND', - 4);
  67. define('MDB2_ERROR_ALREADY_EXISTS', - 5);
  68. define('MDB2_ERROR_UNSUPPORTED', - 6);
  69. define('MDB2_ERROR_MISMATCH', - 7);
  70. define('MDB2_ERROR_INVALID', - 8);
  71. define('MDB2_ERROR_NOT_CAPABLE', - 9);
  72. define('MDB2_ERROR_TRUNCATED', - 10);
  73. define('MDB2_ERROR_INVALID_NUMBER', - 11);
  74. define('MDB2_ERROR_INVALID_DATE', - 12);
  75. define('MDB2_ERROR_DIVZERO', - 13);
  76. define('MDB2_ERROR_NODBSELECTED', - 14);
  77. define('MDB2_ERROR_CANNOT_CREATE', - 15);
  78. define('MDB2_ERROR_CANNOT_DELETE', - 16);
  79. define('MDB2_ERROR_CANNOT_DROP', - 17);
  80. define('MDB2_ERROR_NOSUCHTABLE', - 18);
  81. define('MDB2_ERROR_NOSUCHFIELD', - 19);
  82. define('MDB2_ERROR_NEED_MORE_DATA', - 20);
  83. define('MDB2_ERROR_NOT_LOCKED', - 21);
  84. define('MDB2_ERROR_VALUE_COUNT_ON_ROW', - 22);
  85. define('MDB2_ERROR_INVALID_DSN', - 23);
  86. define('MDB2_ERROR_CONNECT_FAILED', - 24);
  87. define('MDB2_ERROR_EXTENSION_NOT_FOUND', - 25);
  88. define('MDB2_ERROR_NOSUCHDB', - 26);
  89. define('MDB2_ERROR_ACCESS_VIOLATION', - 27);
  90. define('MDB2_ERROR_CANNOT_REPLACE', - 28);
  91. define('MDB2_ERROR_CONSTRAINT_NOT_NULL', - 29);
  92. define('MDB2_ERROR_DEADLOCK', - 30);
  93. define('MDB2_ERROR_CANNOT_ALTER', - 31);
  94. define('MDB2_ERROR_MANAGER', - 32);
  95. define('MDB2_ERROR_MANAGER_PARSE', - 33);
  96. define('MDB2_ERROR_LOADMODULE', - 34);
  97. define('MDB2_ERROR_INSUFFICIENT_DATA', - 35);
  98. define('MDB2_ERROR_NO_PERMISSION', - 36);
  99. define('MDB2_ERROR_DISCONNECT_FAILED', - 37);
  100. // }}}
  101. // {{{ Verbose constants
  102. /**
  103. * These are just helper constants to more verbosely express parameters to prepare()
  104. */
  105. define('MDB2_PREPARE_MANIP', false);
  106. define('MDB2_PREPARE_RESULT', null);
  107. // }}}
  108. // {{{ Fetchmode constants
  109. /**
  110. * This is a special constant that tells MDB2 the user hasn't specified
  111. * any particular get mode, so the default should be used.
  112. */
  113. define('MDB2_FETCHMODE_DEFAULT', 0);
  114. /**
  115. * Column data indexed by numbers, ordered from 0 and up
  116. */
  117. define('MDB2_FETCHMODE_ORDERED', 1);
  118. /**
  119. * Column data indexed by column names
  120. */
  121. define('MDB2_FETCHMODE_ASSOC', 2);
  122. /**
  123. * Column data as object properties
  124. */
  125. define('MDB2_FETCHMODE_OBJECT', 3);
  126. /**
  127. * For multi-dimensional results: normally the first level of arrays
  128. * is the row number, and the second level indexed by column number or name.
  129. * MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays
  130. * is the column name, and the second level the row number.
  131. */
  132. define('MDB2_FETCHMODE_FLIPPED', 4);
  133. // }}}
  134. // {{{ Portability mode constants
  135. /**
  136. * Portability: turn off all portability features.
  137. * @see MDB2_Driver_Common::setOption()
  138. */
  139. define('MDB2_PORTABILITY_NONE', 0);
  140. /**
  141. * Portability: convert names of tables and fields to case defined in the
  142. * "field_case" option when using the query*(), fetch*() and tableInfo() methods.
  143. * @see MDB2_Driver_Common::setOption()
  144. */
  145. define('MDB2_PORTABILITY_FIX_CASE', 1);
  146. /**
  147. * Portability: right trim the data output by query*() and fetch*().
  148. * @see MDB2_Driver_Common::setOption()
  149. */
  150. define('MDB2_PORTABILITY_RTRIM', 2);
  151. /**
  152. * Portability: force reporting the number of rows deleted.
  153. * @see MDB2_Driver_Common::setOption()
  154. */
  155. define('MDB2_PORTABILITY_DELETE_COUNT', 4);
  156. /**
  157. * Portability: not needed in MDB2 (just left here for compatibility to DB)
  158. * @see MDB2_Driver_Common::setOption()
  159. */
  160. define('MDB2_PORTABILITY_NUMROWS', 8);
  161. /**
  162. * Portability: makes certain error messages in certain drivers compatible
  163. * with those from other DBMS's.
  164. *
  165. * + mysql, mysqli: change unique/primary key constraints
  166. * MDB2_ERROR_ALREADY_EXISTS -> MDB2_ERROR_CONSTRAINT
  167. *
  168. * + odbc(access): MS's ODBC driver reports 'no such field' as code
  169. * 07001, which means 'too few parameters.' When this option is on
  170. * that code gets mapped to MDB2_ERROR_NOSUCHFIELD.
  171. *
  172. * @see MDB2_Driver_Common::setOption()
  173. */
  174. define('MDB2_PORTABILITY_ERRORS', 16);
  175. /**
  176. * Portability: convert empty values to null strings in data output by
  177. * query*() and fetch*().
  178. * @see MDB2_Driver_Common::setOption()
  179. */
  180. define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32);
  181. /**
  182. * Portability: removes database/table qualifiers from associative indexes
  183. * @see MDB2_Driver_Common::setOption()
  184. */
  185. define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64);
  186. /**
  187. * Portability: turn on all portability features.
  188. * @see MDB2_Driver_Common::setOption()
  189. */
  190. define('MDB2_PORTABILITY_ALL', 127);
  191. // }}}
  192. // {{{ Globals for class instance tracking
  193. /**
  194. * These are global variables that are used to track the various class instances
  195. */
  196. $GLOBALS['_MDB2_databases'] = array();
  197. $GLOBALS['_MDB2_dsninfo_default'] = array('phptype' => false, 'dbsyntax' => false, 'username' => false,
  198. 'password' => false, 'protocol' => false, 'hostspec' => false, 'port' => false, 'socket' => false,
  199. 'database' => false, 'mode' => false);
  200. // }}}
  201. // {{{ class MDB2
  202. /**
  203. * The main 'MDB2' class is simply a container class with some static
  204. * methods for creating DB objects as well as some utility functions
  205. * common to all parts of DB.
  206. *
  207. * The object model of MDB2 is as follows (indentation means inheritance):
  208. *
  209. * MDB2 The main MDB2 class. This is simply a utility class
  210. * with some 'static' methods for creating MDB2 objects as
  211. * well as common utility functions for other MDB2 classes.
  212. *
  213. * MDB2_Driver_Common The base for each MDB2 implementation. Provides default
  214. * | implementations (in OO lingo virtual methods) for
  215. * | the actual DB implementations as well as a bunch of
  216. * | query utility functions.
  217. * |
  218. * +-MDB2_Driver_mysql The MDB2 implementation for MySQL. Inherits MDB2_Driver_Common.
  219. * When calling MDB2::factory or MDB2::connect for MySQL
  220. * connections, the object returned is an instance of this
  221. * class.
  222. * +-MDB2_Driver_pgsql The MDB2 implementation for PostGreSQL. Inherits MDB2_Driver_Common.
  223. * When calling MDB2::factory or MDB2::connect for PostGreSQL
  224. * connections, the object returned is an instance of this
  225. * class.
  226. *
  227. * @package MDB2
  228. * @category Database
  229. * @author Lukas Smith <smith@pooteeweet.org>
  230. */
  231. class MDB2
  232. {
  233. // {{{ function setOptions(&$db, $options)
  234. /**
  235. * set option array in an exiting database object
  236. *
  237. * @param MDB2_Driver_Common MDB2 object
  238. * @param array An associative array of option names and their values.
  239. *
  240. * @return mixed MDB2_OK or a PEAR Error object
  241. *
  242. * @access public
  243. */
  244. function setOptions(&$db, $options)
  245. {
  246. if (is_array($options))
  247. {
  248. foreach ($options as $option => $value)
  249. {
  250. $test = $db->setOption($option, $value);
  251. if (PEAR :: isError($test))
  252. {
  253. return $test;
  254. }
  255. }
  256. }
  257. return MDB2_OK;
  258. }
  259. // }}}
  260. // {{{ function classExists($classname)
  261. /**
  262. * Checks if a class exists without triggering __autoload
  263. *
  264. * @param string classname
  265. *
  266. * @return bool true success and false on error
  267. * @static
  268. * @access public
  269. */
  270. function classExists($classname)
  271. {
  272. if (version_compare(phpversion(), "5.0", ">="))
  273. {
  274. return class_exists($classname, false);
  275. }
  276. return class_exists($classname);
  277. }
  278. // }}}
  279. // {{{ function loadClass($class_name, $debug)
  280. /**
  281. * Loads a PEAR class.
  282. *
  283. * @param string classname to load
  284. * @param bool if errors should be suppressed
  285. *
  286. * @return mixed true success or PEAR_Error on failure
  287. *
  288. * @access public
  289. */
  290. function loadClass($class_name, $debug)
  291. {
  292. if (! MDB2 :: classExists($class_name))
  293. {
  294. $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php';
  295. if ($debug)
  296. {
  297. $include = include_once ($file_name);
  298. }
  299. else
  300. {
  301. $include = @include_once ($file_name);
  302. }
  303. if (! $include)
  304. {
  305. if (! MDB2 :: fileExists($file_name))
  306. {
  307. $msg = "unable to find package '$class_name' file '$file_name'";
  308. }
  309. else
  310. {
  311. $msg = "unable to load class '$class_name' from file '$file_name'";
  312. }
  313. $err = & MDB2 :: raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg);
  314. return $err;
  315. }
  316. }
  317. return MDB2_OK;
  318. }
  319. // }}}
  320. // {{{ function &factory($dsn, $options = false)
  321. /**
  322. * Create a new MDB2 object for the specified database type
  323. *
  324. * IMPORTANT: In order for MDB2 to work properly it is necessary that
  325. * you make sure that you work with a reference of the original
  326. * object instead of a copy (this is a PHP4 quirk).
  327. *
  328. * For example:
  329. * $db =& MDB2::factory($dsn);
  330. * ^^
  331. * And not:
  332. * $db = MDB2::factory($dsn);
  333. *
  334. * @param mixed 'data source name', see the MDB2::parseDSN
  335. * method for a description of the dsn format.
  336. * Can also be specified as an array of the
  337. * format returned by MDB2::parseDSN.
  338. * @param array An associative array of option names and
  339. * their values.
  340. *
  341. * @return mixed a newly created MDB2 object, or false on error
  342. *
  343. * @access public
  344. */
  345. function &factory($dsn, $options = false)
  346. {
  347. $dsninfo = MDB2 :: parseDSN($dsn);
  348. if (empty($dsninfo['phptype']))
  349. {
  350. $err = & MDB2 :: raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'no RDBMS driver specified');
  351. return $err;
  352. }
  353. $class_name = 'MDB2_Driver_' . $dsninfo['phptype'];
  354. $debug = (! empty($options['debug']));
  355. $err = MDB2 :: loadClass($class_name, $debug);
  356. if (PEAR :: isError($err))
  357. {
  358. return $err;
  359. }
  360. $db = new $class_name();
  361. $db->setDSN($dsninfo);
  362. $err = MDB2 :: setOptions($db, $options);
  363. if (PEAR :: isError($err))
  364. {
  365. return $err;
  366. }
  367. return $db;
  368. }
  369. // }}}
  370. // {{{ function &connect($dsn, $options = false)
  371. /**
  372. * Create a new MDB2_Driver_* connection object and connect to the specified
  373. * database
  374. *
  375. * IMPORTANT: In order for MDB2 to work properly it is necessary that
  376. * you make sure that you work with a reference of the original
  377. * object instead of a copy (this is a PHP4 quirk).
  378. *
  379. * For example:
  380. * $db =& MDB2::connect($dsn);
  381. * ^^
  382. * And not:
  383. * $db = MDB2::connect($dsn);
  384. * ^^
  385. *
  386. * @param mixed $dsn 'data source name', see the MDB2::parseDSN
  387. * method for a description of the dsn format.
  388. * Can also be specified as an array of the
  389. * format returned by MDB2::parseDSN.
  390. * @param array $options An associative array of option names and
  391. * their values.
  392. *
  393. * @return mixed a newly created MDB2 connection object, or a MDB2
  394. * error object on error
  395. *
  396. * @access public
  397. * @see MDB2::parseDSN
  398. */
  399. function &connect($dsn, $options = false)
  400. {
  401. $db = & MDB2 :: factory($dsn, $options);
  402. if (PEAR :: isError($db))
  403. {
  404. return $db;
  405. }
  406. $err = $db->connect();
  407. if (PEAR :: isError($err))
  408. {
  409. $dsn = $db->getDSN('string', 'xxx');
  410. $db->disconnect();
  411. $err->addUserInfo($dsn);
  412. return $err;
  413. }
  414. return $db;
  415. }
  416. // }}}
  417. // {{{ function &singleton($dsn = null, $options = false)
  418. /**
  419. * Returns a MDB2 connection with the requested DSN.
  420. * A new MDB2 connection object is only created if no object with the
  421. * requested DSN exists yet.
  422. *
  423. * IMPORTANT: In order for MDB2 to work properly it is necessary that
  424. * you make sure that you work with a reference of the original
  425. * object instead of a copy (this is a PHP4 quirk).
  426. *
  427. * For example:
  428. * $db =& MDB2::singleton($dsn);
  429. * ^^
  430. * And not:
  431. * $db = MDB2::singleton($dsn);
  432. * ^^
  433. *
  434. * @param mixed 'data source name', see the MDB2::parseDSN
  435. * method for a description of the dsn format.
  436. * Can also be specified as an array of the
  437. * format returned by MDB2::parseDSN.
  438. * @param array An associative array of option names and
  439. * their values.
  440. *
  441. * @return mixed a newly created MDB2 connection object, or a MDB2
  442. * error object on error
  443. *
  444. * @access public
  445. * @see MDB2::parseDSN
  446. */
  447. function &singleton($dsn = null, $options = false)
  448. {
  449. if ($dsn)
  450. {
  451. $dsninfo = MDB2 :: parseDSN($dsn);
  452. $dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo);
  453. $keys = array_keys($GLOBALS['_MDB2_databases']);
  454. for($i = 0, $j = count($keys); $i < $j; ++ $i)
  455. {
  456. if (isset($GLOBALS['_MDB2_databases'][$keys[$i]]))
  457. {
  458. $tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('array');
  459. if (count(array_diff_assoc($tmp_dsn, $dsninfo)) == 0)
  460. {
  461. MDB2 :: setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options);
  462. return $GLOBALS['_MDB2_databases'][$keys[$i]];
  463. }
  464. }
  465. }
  466. }
  467. elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases']))
  468. {
  469. $db = & $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])];
  470. return $db;
  471. }
  472. $db = & MDB2 :: factory($dsn, $options);
  473. return $db;
  474. }
  475. // }}}
  476. // {{{ function areEquals()
  477. /**
  478. * It looks like there's a memory leak in array_diff() in PHP 5.1.x,
  479. * so use this method instead.
  480. * @see http://pear.php.net/bugs/bug.php?id=11790
  481. *
  482. * @param array $arr1
  483. * @param array $arr2
  484. * @return boolean
  485. */
  486. function areEquals($arr1, $arr2)
  487. {
  488. if (count($arr1) != count($arr2))
  489. {
  490. return false;
  491. }
  492. foreach (array_keys($arr1) as $k)
  493. {
  494. if (! array_key_exists($k, $arr2) || $arr1[$k] != $arr2[$k])
  495. {
  496. return false;
  497. }
  498. }
  499. return true;
  500. }
  501. // }}}
  502. // {{{ function loadFile($file)
  503. /**
  504. * load a file (like 'Date')
  505. *
  506. * @param string name of the file in the MDB2 directory (without '.php')
  507. *
  508. * @return string name of the file that was included
  509. *
  510. * @access public
  511. */
  512. function loadFile($file)
  513. {
  514. $file_name = 'MDB2' . DIRECTORY_SEPARATOR . $file . '.php';
  515. if (! MDB2 :: fileExists($file_name))
  516. {
  517. return MDB2 :: raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'unable to find: ' . $file_name);
  518. }
  519. if (! include_once ($file_name))
  520. {
  521. return MDB2 :: raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'unable to load driver class: ' . $file_name);
  522. }
  523. return $file_name;
  524. }
  525. // }}}
  526. // {{{ function apiVersion()
  527. /**
  528. * Return the MDB2 API version
  529. *
  530. * @return string the MDB2 API version number
  531. *
  532. * @access public
  533. */
  534. function apiVersion()
  535. {
  536. return '2.5.0b2';
  537. }
  538. // }}}
  539. // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
  540. /**
  541. * This method is used to communicate an error and invoke error
  542. * callbacks etc. Basically a wrapper for PEAR::raiseError
  543. * without the message string.
  544. *
  545. * @param mixed int error code
  546. *
  547. * @param int error mode, see PEAR_Error docs
  548. *
  549. * @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the
  550. * error level (E_USER_NOTICE etc). If error mode is
  551. * PEAR_ERROR_CALLBACK, this is the callback function,
  552. * either as a function name, or as an array of an
  553. * object and method name. For other error modes this
  554. * parameter is ignored.
  555. *
  556. * @param string Extra debug information. Defaults to the last
  557. * query and native error code.
  558. *
  559. * @return PEAR_Error instance of a PEAR Error object
  560. *
  561. * @access private
  562. * @see PEAR_Error
  563. */
  564. function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $dummy1 = null, $dummy2 = null, $dummy3 = false)
  565. {
  566. $err = & PEAR :: raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
  567. return $err;
  568. }
  569. // }}}
  570. // {{{ function isError($data, $code = null)
  571. /**
  572. * Tell whether a value is a MDB2 error.
  573. *
  574. * @param mixed the value to test
  575. * @param int if is an error object, return true
  576. * only if $code is a string and
  577. * $db->getMessage() == $code or
  578. * $code is an integer and $db->getCode() == $code
  579. *
  580. * @return bool true if parameter is an error
  581. *
  582. * @access public
  583. */
  584. function isError($data, $code = null)
  585. {
  586. if (is_a($data, 'MDB2_Error'))
  587. {
  588. if (is_null($code))
  589. {
  590. return true;
  591. }
  592. elseif (is_string($code))
  593. {
  594. return $data->getMessage() === $code;
  595. }
  596. else
  597. {
  598. $code = (array) $code;
  599. return in_array($data->getCode(), $code);
  600. }
  601. }
  602. return false;
  603. }
  604. // }}}
  605. // {{{ function isConnection($value)
  606. /**
  607. * Tell whether a value is a MDB2 connection
  608. *
  609. * @param mixed value to test
  610. *
  611. * @return bool whether $value is a MDB2 connection
  612. *
  613. * @access public
  614. */
  615. function isConnection($value)
  616. {
  617. return is_a($value, 'MDB2_Driver_Common');
  618. }
  619. // }}}
  620. // {{{ function isResult($value)
  621. /**
  622. * Tell whether a value is a MDB2 result
  623. *
  624. * @param mixed value to test
  625. *
  626. * @return bool whether $value is a MDB2 result
  627. *
  628. * @access public
  629. */
  630. function isResult($value)
  631. {
  632. return is_a($value, 'MDB2_Result');
  633. }
  634. // }}}
  635. // {{{ function isResultCommon($value)
  636. /**
  637. * Tell whether a value is a MDB2 result implementing the common interface
  638. *
  639. * @param mixed value to test
  640. *
  641. * @return bool whether $value is a MDB2 result implementing the common interface
  642. *
  643. * @access public
  644. */
  645. function isResultCommon($value)
  646. {
  647. return is_a($value, 'MDB2_Result_Common');
  648. }
  649. // }}}
  650. // {{{ function isStatement($value)
  651. /**
  652. * Tell whether a value is a MDB2 statement interface
  653. *
  654. * @param mixed value to test
  655. *
  656. * @return bool whether $value is a MDB2 statement interface
  657. *
  658. * @access public
  659. */
  660. function isStatement($value)
  661. {
  662. return is_a($value, 'MDB2_Statement_Common');
  663. }
  664. // }}}
  665. // {{{ function errorMessage($value = null)
  666. /**
  667. * Return a textual error message for a MDB2 error code
  668. *
  669. * @param int|array integer error code,
  670. null to get the current error code-message map,
  671. or an array with a new error code-message map
  672. *
  673. * @return string error message, or false if the error code was
  674. * not recognized
  675. *
  676. * @access public
  677. */
  678. function errorMessage($value = null)
  679. {
  680. static $errorMessages;
  681. if (is_array($value))
  682. {
  683. $errorMessages = $value;
  684. return MDB2_OK;
  685. }
  686. if (! isset($errorMessages))
  687. {
  688. $errorMessages = array(MDB2_OK => 'no error', MDB2_ERROR => 'unknown error',
  689. MDB2_ERROR_ALREADY_EXISTS => 'already exists', MDB2_ERROR_CANNOT_CREATE => 'can not create',
  690. MDB2_ERROR_CANNOT_ALTER => 'can not alter', MDB2_ERROR_CANNOT_REPLACE => 'can not replace',
  691. MDB2_ERROR_CANNOT_DELETE => 'can not delete', MDB2_ERROR_CANNOT_DROP => 'can not drop',
  692. MDB2_ERROR_CONSTRAINT => 'constraint violation',
  693. MDB2_ERROR_CONSTRAINT_NOT_NULL => 'null value violates not-null constraint',
  694. MDB2_ERROR_DIVZERO => 'division by zero', MDB2_ERROR_INVALID => 'invalid',
  695. MDB2_ERROR_INVALID_DATE => 'invalid date or time', MDB2_ERROR_INVALID_NUMBER => 'invalid number',
  696. MDB2_ERROR_MISMATCH => 'mismatch', MDB2_ERROR_NODBSELECTED => 'no database selected',
  697. MDB2_ERROR_NOSUCHFIELD => 'no such field', MDB2_ERROR_NOSUCHTABLE => 'no such table',
  698. MDB2_ERROR_NOT_CAPABLE => 'MDB2 backend not capable', MDB2_ERROR_NOT_FOUND => 'not found',
  699. MDB2_ERROR_NOT_LOCKED => 'not locked', MDB2_ERROR_SYNTAX => 'syntax error',
  700. MDB2_ERROR_UNSUPPORTED => 'not supported', MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
  701. MDB2_ERROR_INVALID_DSN => 'invalid DSN', MDB2_ERROR_CONNECT_FAILED => 'connect failed',
  702. MDB2_ERROR_NEED_MORE_DATA => 'insufficient data supplied',
  703. MDB2_ERROR_EXTENSION_NOT_FOUND => 'extension not found',
  704. MDB2_ERROR_NOSUCHDB => 'no such database',
  705. MDB2_ERROR_ACCESS_VIOLATION => 'insufficient permissions',
  706. MDB2_ERROR_LOADMODULE => 'error while including on demand module',
  707. MDB2_ERROR_TRUNCATED => 'truncated', MDB2_ERROR_DEADLOCK => 'deadlock detected',
  708. MDB2_ERROR_NO_PERMISSION => 'no permission', MDB2_ERROR_DISCONNECT_FAILED => 'disconnect failed');
  709. }
  710. if (is_null($value))
  711. {
  712. return $errorMessages;
  713. }
  714. if (PEAR :: isError($value))
  715. {
  716. $value = $value->getCode();
  717. }
  718. return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[MDB2_ERROR];
  719. }
  720. // }}}
  721. // {{{ function parseDSN($dsn)
  722. /**
  723. * Parse a data source name.
  724. *
  725. * Additional keys can be added by appending a URI query string to the
  726. * end of the DSN.
  727. *
  728. * The format of the supplied DSN is in its fullest form:
  729. * <code>
  730. * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
  731. * </code>
  732. *
  733. * Most variations are allowed:
  734. * <code>
  735. * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
  736. * phptype://username:password@hostspec/database_name
  737. * phptype://username:password@hostspec
  738. * phptype://username@hostspec
  739. * phptype://hostspec/database
  740. * phptype://hostspec
  741. * phptype(dbsyntax)
  742. * phptype
  743. * </code>
  744. *
  745. * @param string Data Source Name to be parsed
  746. *
  747. * @return array an associative array with the following keys:
  748. * + phptype: Database backend used in PHP (mysql, odbc etc.)
  749. * + dbsyntax: Database used with regards to SQL syntax etc.
  750. * + protocol: Communication protocol to use (tcp, unix etc.)
  751. * + hostspec: Host specification (hostname[:port])
  752. * + database: Database to use on the DBMS server
  753. * + username: User name for login
  754. * + password: Password for login
  755. *
  756. * @access public
  757. * @author Tomas V.V.Cox <cox@idecnet.com>
  758. */
  759. function parseDSN($dsn)
  760. {
  761. $parsed = $GLOBALS['_MDB2_dsninfo_default'];
  762. if (is_array($dsn))
  763. {
  764. $dsn = array_merge($parsed, $dsn);
  765. if (! $dsn['dbsyntax'])
  766. {
  767. $dsn['dbsyntax'] = $dsn['phptype'];
  768. }
  769. return $dsn;
  770. }
  771. // Find phptype and dbsyntax
  772. if (($pos = strpos($dsn, '://')) !== false)
  773. {
  774. $str = substr($dsn, 0, $pos);
  775. $dsn = substr($dsn, $pos + 3);
  776. }
  777. else
  778. {
  779. $str = $dsn;
  780. $dsn = null;
  781. }
  782. // Get phptype and dbsyntax
  783. // $str => phptype(dbsyntax)
  784. if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr))
  785. {
  786. $parsed['phptype'] = $arr[1];
  787. $parsed['dbsyntax'] = ! $arr[2] ? $arr[1] : $arr[2];
  788. }
  789. else
  790. {
  791. $parsed['phptype'] = $str;
  792. $parsed['dbsyntax'] = $str;
  793. }
  794. if (! count($dsn))
  795. {
  796. return $parsed;
  797. }
  798. // Get (if found): username and password
  799. // $dsn => username:password@protocol+hostspec/database
  800. if (($at = strrpos($dsn, '@')) !== false)
  801. {
  802. $str = substr($dsn, 0, $at);
  803. $dsn = substr($dsn, $at + 1);
  804. if (($pos = strpos($str, ':')) !== false)
  805. {
  806. $parsed['username'] = rawurldecode(substr($str, 0, $pos));
  807. $parsed['password'] = rawurldecode(substr($str, $pos + 1));
  808. }
  809. else
  810. {
  811. $parsed['username'] = rawurldecode($str);
  812. }
  813. }
  814. // Find protocol and hostspec
  815. // $dsn => proto(proto_opts)/database
  816. if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match))
  817. {
  818. $proto = $match[1];
  819. $proto_opts = $match[2] ? $match[2] : false;
  820. $dsn = $match[3];
  821. // $dsn => protocol+hostspec/database (old format)
  822. }
  823. else
  824. {
  825. if (strpos($dsn, '+') !== false)
  826. {
  827. list($proto, $dsn) = explode('+', $dsn, 2);
  828. }
  829. if (strpos($dsn, '//') === 0 && strpos($dsn, '/', 2) !== false && $parsed['phptype'] == 'oci8')
  830. {
  831. //oracle's "Easy Connect" syntax:
  832. //"username/password@[//]host[:port][/service_name]"
  833. //e.g. "scott/tiger@//mymachine:1521/oracle"
  834. $proto_opts = $dsn;
  835. $dsn = substr($proto_opts, strrpos($proto_opts, '/') + 1);
  836. }
  837. elseif (strpos($dsn, '/') !== false)
  838. {
  839. list($proto_opts, $dsn) = explode('/', $dsn, 2);
  840. }
  841. else
  842. {
  843. $proto_opts = $dsn;
  844. $dsn = null;
  845. }
  846. }
  847. // process the different protocol options
  848. $parsed['protocol'] = (! empty($proto)) ? $proto : 'tcp';
  849. $proto_opts = rawurldecode($proto_opts);
  850. if (strpos($proto_opts, ':') !== false)
  851. {
  852. list($proto_opts, $parsed['port']) = explode(':', $proto_opts);
  853. }
  854. if ($parsed['protocol'] == 'tcp')
  855. {
  856. $parsed['hostspec'] = $proto_opts;
  857. }
  858. elseif ($parsed['protocol'] == 'unix')
  859. {
  860. $parsed['socket'] = $proto_opts;
  861. }
  862. // Get dabase if any
  863. // $dsn => database
  864. if ($dsn)
  865. {
  866. // /database
  867. if (($pos = strpos($dsn, '?')) === false)
  868. {
  869. $parsed['database'] = $dsn;
  870. // /database?param1=value1&param2=value2
  871. }
  872. else
  873. {
  874. $parsed['database'] = substr($dsn, 0, $pos);
  875. $dsn = substr($dsn, $pos + 1);
  876. if (strpos($dsn, '&') !== false)
  877. {
  878. $opts = explode('&', $dsn);
  879. }
  880. else
  881. { // database?param1=value1
  882. $opts = array($dsn);
  883. }
  884. foreach ($opts as $opt)
  885. {
  886. list($key, $value) = explode('=', $opt);
  887. if (! isset($parsed[$key]))
  888. {
  889. // don't allow params overwrite
  890. $parsed[$key] = rawurldecode($value);
  891. }
  892. }
  893. }
  894. }
  895. return $parsed;
  896. }
  897. // }}}
  898. // {{{ function fileExists($file)
  899. /**
  900. * Checks if a file exists in the include path
  901. *
  902. * @param string filename
  903. *
  904. * @return bool true success and false on error
  905. *
  906. * @access public
  907. */
  908. function fileExists($file)
  909. {
  910. // safe_mode does notwork with is_readable()
  911. if (! @ini_get('safe_mode'))
  912. {
  913. $dirs = explode(PATH_SEPARATOR, ini_get('include_path'));
  914. foreach ($dirs as $dir)
  915. {
  916. if (is_readable($dir . DIRECTORY_SEPARATOR . $file))
  917. {
  918. return true;
  919. }
  920. }
  921. }
  922. else
  923. {
  924. $fp = @fopen($file, 'r', true);
  925. if (is_resource($fp))
  926. {
  927. @fclose($fp);
  928. return true;
  929. }
  930. }
  931. return false;
  932. }
  933. // }}}
  934. }
  935. // }}}
  936. // {{{ class MDB2_Error extends PEAR_Error
  937. /**
  938. * MDB2_Error implements a class for reporting portable database error
  939. * messages.
  940. *
  941. * @package MDB2
  942. * @category Database
  943. * @author Stig Bakken <ssb@fast.no>
  944. */
  945. class MDB2_Error extends PEAR_Error
  946. {
  947. // {{{ constructor: function __construct($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null)
  948. /**
  949. * MDB2_Error constructor.
  950. *
  951. * @param mixed MDB2 error code, or string with error message.
  952. * @param int what 'error mode' to operate in
  953. * @param int what error level to use for $mode & PEAR_ERROR_TRIGGER
  954. * @param mixed additional debug info, such as the last query
  955. */
  956. function __construct($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null, $dummy = null)
  957. {
  958. if (is_null($code))
  959. {
  960. $code = MDB2_ERROR;
  961. }
  962. parent :: __construct('MDB2 Error: ' . MDB2 :: errorMessage($code), $code, $mode, $level, $debuginfo);
  963. }
  964. // }}}
  965. }
  966. // }}}
  967. // {{{ class MDB2_Driver_Common extends PEAR
  968. /**
  969. * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
  970. *
  971. * @package MDB2
  972. * @category Database
  973. * @author Lukas Smith <smith@pooteeweet.org>
  974. */
  975. class MDB2_Driver_Common extends PEAR
  976. {
  977. // {{{ Variables (Properties)
  978. /**
  979. * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array
  980. * @var int
  981. * @access public
  982. */
  983. var $db_index = 0;
  984. /**
  985. * DSN used for the next query
  986. * @var array
  987. * @access protected
  988. */
  989. var $dsn = array();
  990. /**
  991. * DSN that was used to create the current connection
  992. * @var array
  993. * @access protected
  994. */
  995. var $connected_dsn = array();
  996. /**
  997. * connection resource
  998. * @var mixed
  999. * @access protected
  1000. */
  1001. var $connection = 0;
  1002. /**
  1003. * if the current opened connection is a persistent connection
  1004. * @var bool
  1005. * @access protected
  1006. */
  1007. var $opened_persistent;
  1008. /**
  1009. * the name of the database for the next query
  1010. * @var string
  1011. * @access protected
  1012. */
  1013. var $database_name = '';
  1014. /**
  1015. * the name of the database currently selected
  1016. * @var string
  1017. * @access protected
  1018. */
  1019. var $connected_database_name = '';
  1020. /**
  1021. * server version information
  1022. * @var string
  1023. * @access protected
  1024. */
  1025. var $connected_server_info = '';
  1026. /**
  1027. * list of all supported features of the given driver
  1028. * @var array
  1029. * @access public
  1030. */
  1031. var $supported = array('sequences' => false, 'indexes' => false, 'affected_rows' => false,
  1032. 'summary_functions' => false, 'order_by_text' => false, 'transactions' => false, 'savepoints' => false,
  1033. 'current_id' => false, 'limit_queries' => false, 'LOBs' => false, 'replace' => false, 'sub_selects' => false,
  1034. 'triggers' => false, 'auto_increment' => false, 'primary_key' => false, 'result_introspection' => false,
  1035. 'prepared_statements' => false, 'identifier_quoting' => false, 'pattern_escaping' => false,
  1036. 'new_link' => false);
  1037. /**
  1038. * Array of supported options that can be passed to the MDB2 instance.
  1039. *
  1040. * The options can be set during object creation, using
  1041. * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can
  1042. * also be set after the object is created, using MDB2::setOptions() or
  1043. * MDB2_Driver_Common::setOption().
  1044. * The list of available option includes:
  1045. * <ul>
  1046. * <li>$options['ssl'] -> boolean: determines if ssl should be used for connections</li>
  1047. * <li>$options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what case to force on field/table names</li>
  1048. * <li>$options['disable_query'] -> boolean: determines if queries should be executed</li>
  1049. * <li>$options['result_class'] -> string: class used for result sets</li>
  1050. * <li>$options['buffered_result_class'] -> string: class used for buffered result sets</li>
  1051. * <li>$options['result_wrap_class'] -> string: class used to wrap result sets into</li>
  1052. * <li>$options['result_buffering'] -> boolean should results be buffered or not?</li>
  1053. * <li>$options['fetch_class'] -> string: class to use when fetch mode object is used</li>
  1054. * <li>$options['persistent'] -> boolean: persistent connection?</li>
  1055. * <li>$options['debug'] -> integer: numeric debug level</li>
  1056. * <li>$options['debug_handler'] -> string: function/method that captures debug messages</li>
  1057. * <li>$options['debug_expanded_output'] -> bool: BC option to determine if more context information should be send to the debug handler</li>
  1058. * <li>$options['default_text_field_length'] -> integer: default text field length to use</li>
  1059. * <li>$options['lob_buffer_length'] -> integer: LOB buffer length</li>
  1060. * <li>$options['log_line_break'] -> string: line-break format</li>
  1061. * <li>$options['idxname_format'] -> string: pattern for index name</li>
  1062. * <li>$options['seqname_format'] -> string: pattern for sequence name</li>
  1063. * <li>$options['savepoint_format'] -> string: pattern for auto generated savepoint names</li>
  1064. * <li>$options['statement_format'] -> string: pattern for prepared statement names</li>
  1065. * <li>$options['seqcol_name'] -> string: sequence column name</li>
  1066. * <li>$options['quote_identifier'] -> boolean: if identifier quoting should be done when check_option is used</li>
  1067. * <li>$options['use_transactions'] -> boolean: if transaction use should be enabled</li>
  1068. * <li>$options['decimal_places'] -> integer: number of decimal places to handle</li>
  1069. * <li>$options['portability'] -> integer: portability constant</li>
  1070. * <li>$options['modules'] -> array: short to long module name mapping for __call()</li>
  1071. * <li>$options['emulate_prepared'] -> boolean: force prepared statements to be emulated</li>
  1072. * <li>$options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes</li>
  1073. * <li>$options['datatype_map_callback'] -> array: callback function/method that should be called</li>
  1074. * <li>$options['bindname_format'] -> string: regular expression pattern for named parameters</li>
  1075. * <li>$options['multi_query'] -> boolean: determines if queries returning multiple result sets should be executed</li>
  1076. * <li>$options['max_identifiers_length'] -> integer: max identifier length</li>
  1077. * <li>$options['default_fk_action_onupdate'] -> string: default FOREIGN KEY ON UPDATE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li>
  1078. * <li>$options['default_fk_action_ondelete'] -> string: default FOREIGN KEY ON DELETE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li>
  1079. * </ul>
  1080. *
  1081. * @var array
  1082. * @access public
  1083. * @see MDB2::connect()
  1084. * @see MDB2::factory()
  1085. * @see MDB2::singleton()
  1086. * @see MDB2_Driver_Common::setOption()
  1087. */
  1088. var $options = array('ssl' => false, 'field_case' => CASE_LOWER, 'disable_query' => false,
  1089. 'result_class' => 'MDB2_Result_%s', 'buffered_result_class' => 'MDB2_BufferedResult_%s',
  1090. 'result_wrap_class' => false, 'result_buffering' => true, 'fetch_class' => 'stdClass', 'persistent' => false,
  1091. 'debug' => 0, 'debug_handler' => 'MDB2_defaultDebugOutput', 'debug_expanded_output' => false,
  1092. 'default_text_field_length' => 4096, 'lob_buffer_length' => 8192, 'log_line_break' => "\n",
  1093. 'idxname_format' => '%s_idx', 'seqname_format' => '%s_seq', 'savepoint_format' => 'MDB2_SAVEPOINT_%s',
  1094. 'statement_format' => 'MDB2_STATEMENT_%1$s_%2$s', 'seqcol_name' => 'sequence', 'quote_identifier' => false,
  1095. 'use_transactions' => true, 'decimal_places' => 2, 'portability' => MDB2_PORTABILITY_ALL,
  1096. 'modules' => array('ex' => 'Extended', 'dt' => 'Datatype', 'mg' => 'Manager', 'rv' => 'Reverse',
  1097. 'na' => 'Native', 'fc' => 'Function'), 'emulate_prepared' => false, 'datatype_map' => array(),
  1098. 'datatype_map_callback' => array(), 'nativetype_map_callback' => array(), 'lob_allow_url_include' => false,
  1099. 'bindname_format' => '(?:\d+)|(?:[a-zA-Z][a-zA-Z0-9_]*)', 'max_identifiers_length' => 30,
  1100. 'default_fk_action_onupdate' => 'RESTRICT', 'default_fk_action_ondelete' => 'RESTRICT');
  1101. /**
  1102. * string array
  1103. * @var string
  1104. * @access protected
  1105. */
  1106. var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false);
  1107. /**
  1108. * identifier quoting
  1109. * @var array
  1110. * @access protected
  1111. */
  1112. var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
  1113. /**
  1114. * sql comments
  1115. * @var array
  1116. * @access protected
  1117. */
  1118. var $sql_comments = array(array('start' => '--', 'end' => "\n", 'escape' => false),
  1119. array('start' => '/*', 'end' => '*/', 'escape' => false));
  1120. /**
  1121. * comparision wildcards
  1122. * @var array
  1123. * @access protected
  1124. */
  1125. var $wildcards = array('%', '_');
  1126. /**
  1127. * column alias keyword
  1128. * @var string
  1129. * @access protected
  1130. */
  1131. var $as_keyword = ' AS ';
  1132. /**
  1133. * warnings
  1134. * @var array
  1135. * @access protected
  1136. */
  1137. var $warnings = array();
  1138. /**
  1139. * string with the debugging information
  1140. * @var string
  1141. * @access public
  1142. */
  1143. var $debug_output = '';
  1144. /**
  1145. * determine if there is an open transaction
  1146. * @var bool
  1147. * @access protected
  1148. */
  1149. var $in_transaction = false;
  1150. /**
  1151. * the smart transaction nesting depth
  1152. * @var int
  1153. * @access protected
  1154. */
  1155. var $nested_transaction_counter = null;
  1156. /**
  1157. * the first error that occured inside a nested transaction
  1158. * @var MDB2_Error|bool
  1159. * @access protected
  1160. */
  1161. var $has_transaction_error = false;
  1162. /**
  1163. * result offset used in the next query
  1164. * @var int
  1165. * @access protected
  1166. */
  1167. var $offset = 0;
  1168. /**
  1169. * result limit used in the next query
  1170. * @var int
  1171. * @access protected
  1172. */
  1173. var $limit = 0;
  1174. /**
  1175. * Database backend used in PHP (mysql, odbc etc.)
  1176. * @var string
  1177. * @access public
  1178. */
  1179. var $phptype;
  1180. /**
  1181. * Database used with regards to SQL syntax etc.
  1182. * @var string
  1183. * @access public
  1184. */
  1185. var $dbsyntax;
  1186. /**
  1187. * the last query sent to the driver
  1188. * @var string
  1189. * @access public
  1190. */
  1191. var $last_query;
  1192. /**
  1193. * the default fetchmode used
  1194. * @var int
  1195. * @access protected
  1196. */
  1197. var $fetchmode = MDB2_FETCHMODE_ORDERED;
  1198. /**
  1199. * array of module instances
  1200. * @var array
  1201. * @access protected
  1202. */
  1203. var $modules = array();
  1204. /**
  1205. * determines of the PHP4 destructor emulation has been enabled yet
  1206. * @var array
  1207. * @access protected
  1208. */
  1209. var $destructor_registered = true;
  1210. // }}}
  1211. // {{{ constructor: function __construct()
  1212. /**
  1213. * Constructor
  1214. */
  1215. function __construct()
  1216. {
  1217. end($GLOBALS['_MDB2_databases']);
  1218. $db_index = key($GLOBALS['_MDB2_databases']) + 1;
  1219. $GLOBALS['_MDB2_databases'][$db_index] = &$this;
  1220. $this->db_index = $db_index;
  1221. }
  1222. // }}}
  1223. // {{{ function __construct()
  1224. /**
  1225. * PHP 4 Constructor
  1226. */
  1227. // function __construct()
  1228. // {
  1229. // $this->destructor_registered = false;
  1230. // $this->__construct();
  1231. // }
  1232. // }}}
  1233. // {{{ destructor: function __destruct()
  1234. /**
  1235. * Destructor
  1236. */
  1237. function __destruct()
  1238. {
  1239. $this->disconnect(false);
  1240. }
  1241. // }}}
  1242. // {{{ function free()
  1243. /**
  1244. * Free the internal references so that the instance can be destroyed
  1245. *
  1246. * @return bool true on success, false if result is invalid
  1247. *
  1248. * @access public
  1249. */
  1250. function free()
  1251. {
  1252. unset($GLOBALS['_MDB2_databases'][$this->db_index]);
  1253. unset($this->db_index);
  1254. return MDB2_OK;
  1255. }
  1256. // }}}
  1257. // {{{ function __toString()
  1258. /**
  1259. * String conversation
  1260. *
  1261. * @return string representation of the object
  1262. *
  1263. * @access public
  1264. */
  1265. function __toString()
  1266. {
  1267. $info = get_class($this);
  1268. $info .= ': (phptype = ' . $this->phptype . ', dbsyntax = ' . $this->dbsyntax . ')';
  1269. if ($this->connection)
  1270. {
  1271. $info .= ' [connected]';
  1272. }
  1273. return $info;
  1274. }
  1275. // }}}
  1276. // {{{ function errorInfo($error = null)
  1277. /**
  1278. * This method is used to collect information about an error
  1279. *
  1280. * @param mixed error code or resource
  1281. *
  1282. * @return array with MDB2 errorcode, native error code, native message
  1283. *
  1284. * @access public
  1285. */
  1286. function errorInfo($error = null)
  1287. {
  1288. return array($error, null, null);
  1289. }
  1290. // }}}
  1291. // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
  1292. /**
  1293. * This method is used to communicate an error and invoke error
  1294. * callbacks etc. Basically a wrapper for PEAR::raiseError
  1295. * without the message string.
  1296. *
  1297. * @param mixed $code integer error code, or a PEAR error object (all
  1298. * other parameters are ignored if this parameter is
  1299. * an object
  1300. * @param int $mode error mode, see PEAR_Error docs
  1301. * @param mixed $options If error mode is PEAR_ERROR_TRIGGER, this is the
  1302. * error level (E_USER_NOTICE etc). If error mode is
  1303. * PEAR_ERROR_CALLBACK, this is the callback function,
  1304. * either as a function name, or as an array of an
  1305. * object and method name. For other error modes this
  1306. * parameter is ignored.
  1307. * @param string $userinfo Extra debug information. Defaults to the last
  1308. * query and native error code.
  1309. * @param string $method name of the method that triggered the error
  1310. * @param string $dummy1 not used
  1311. * @param bool $dummy2 not used
  1312. *
  1313. * @return PEAR_Error instance of a PEAR Error object
  1314. * @access public
  1315. * @see PEAR_Error
  1316. */
  1317. function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $method = null, $dummy1 = null, $dummy2 = false)
  1318. {
  1319. $userinfo = "[Error message: $userinfo]\n";
  1320. // The error is yet a MDB2 error object
  1321. if (PEAR :: isError($code))
  1322. {
  1323. // because we use the static PEAR::raiseError, our global
  1324. // handler should be used if it is set
  1325. if (is_null($mode) && ! empty($this->_default_error_mode))
  1326. {
  1327. $mode = $this->_default_error_mode;
  1328. $options = $this->_default_error_options;
  1329. }
  1330. if (is_null($userinfo))
  1331. {
  1332. $userinfo = $code->getUserinfo();
  1333. }
  1334. $code = $code->getCode();
  1335. }
  1336. elseif ($code == MDB2_ERROR_NOT_FOUND)
  1337. {
  1338. // extension not loaded: don't call $this->errorInfo() or the script
  1339. // will die
  1340. }
  1341. elseif (isset($this->connection))
  1342. {
  1343. if (! empty($this->last_query))
  1344. {
  1345. $userinfo .= "[Last executed query: {$this->last_query}]\n";
  1346. }
  1347. $native_errno = $native_msg = null;
  1348. list($code, $native_errno, $native_msg) = $this->errorInfo($code);
  1349. if (! is_null($native_errno) && $native_errno !== '')
  1350. {
  1351. $userinfo .= "[Native code: $native_errno]\n";
  1352. }
  1353. if (! is_null($native_msg) && $native_msg !== '')
  1354. {
  1355. $userinfo .= "[Native message: " . strip_tags($native_msg) . "]\n";
  1356. }
  1357. if (! is_null($method))
  1358. {
  1359. $userinfo = $method . ': ' . $userinfo;
  1360. }
  1361. }
  1362. $err = & PEAR :: raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
  1363. if ($err->getMode() !== PEAR_ERROR_RETURN && isset($this->nested_transaction_counter) && ! $this->has_transaction_error)
  1364. {
  1365. $this->has_transaction_error = & $err;
  1366. }
  1367. return $err;
  1368. }
  1369. // }}}
  1370. // {{{ function resetWarnings()
  1371. /**
  1372. * reset the warning array
  1373. *
  1374. * @return void
  1375. *
  1376. * @access public
  1377. */
  1378. function resetWarnings()
  1379. {
  1380. $this->warnings = array();
  1381. }
  1382. // }}}
  1383. // {{{ function getWarnings()
  1384. /**
  1385. * Get all warnings in reverse order.
  1386. * This means that the last warning is the first element in the array
  1387. *
  1388. * @return array with warnings
  1389. *
  1390. * @access public
  1391. * @see resetWarnings()
  1392. */
  1393. function getWarnings()
  1394. {
  1395. return array_reverse($this->warnings);
  1396. }
  1397. // }}}
  1398. // {{{ function setFetchMode($fetchmode, $object_class = 'stdClass')
  1399. /**
  1400. * Sets which fetch mode should be used by default on queries
  1401. * on this connection
  1402. *
  1403. * @param int MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC
  1404. * or MDB2_FETCHMODE_OBJECT
  1405. * @param string the class name of the object to be returned
  1406. * by the fetch methods when the
  1407. * MDB2_FETCHMODE_OBJECT mode is selected.
  1408. * If no class is specified by default a cast
  1409. * to object from the assoc array row will be
  1410. * done. There is also the possibility to use
  1411. * and extend the 'MDB2_row' class.
  1412. *
  1413. * @return mixed MDB2_OK or MDB2 Error Object
  1414. *
  1415. * @access public
  1416. * @see MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJECT
  1417. */
  1418. function setFetchMode($fetchmode, $object_class = 'stdClass')
  1419. {
  1420. switch ($fetchmode)
  1421. {
  1422. case MDB2_FETCHMODE_OBJECT :
  1423. $this->options['fetch_class'] = $object_class;
  1424. case MDB2_FETCHMODE_ORDERED :
  1425. case MDB2_FETCHMODE_ASSOC :
  1426. $this->fetchmode = $fetchmode;
  1427. break;
  1428. default :
  1429. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'invalid fetchmode mode', __FUNCTION__);
  1430. }
  1431. return MDB2_OK;
  1432. }
  1433. // }}}
  1434. // {{{ function setOption($option, $value)
  1435. /**
  1436. * set the option for the db class
  1437. *
  1438. * @param string option name
  1439. * @param mixed value for the option
  1440. *
  1441. * @return mixed MDB2_OK or MDB2 Error Object
  1442. *
  1443. * @access public
  1444. */
  1445. function setOption($option, $value)
  1446. {
  1447. if (array_key_exists($option, $this->options))
  1448. {
  1449. $this->options[$option] = $value;
  1450. return MDB2_OK;
  1451. }
  1452. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown option $option", __FUNCTION__);
  1453. }
  1454. // }}}
  1455. // {{{ function getOption($option)
  1456. /**
  1457. * Returns the value of an option
  1458. *
  1459. * @param string option name
  1460. *
  1461. * @return mixed the option value or error object
  1462. *
  1463. * @access public
  1464. */
  1465. function getOption($option)
  1466. {
  1467. if (array_key_exists($option, $this->options))
  1468. {
  1469. return $this->options[$option];
  1470. }
  1471. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown option $option", __FUNCTION__);
  1472. }
  1473. // }}}
  1474. // {{{ function debug($message, $scope = '', $is_manip = null)
  1475. /**
  1476. * set a debug message
  1477. *
  1478. * @param string message that should be appended to the debug variable
  1479. * @param string usually the method name that triggered the debug call:
  1480. * for example 'query', 'prepare', 'execute', 'parameters',
  1481. * 'beginTransaction', 'commit', 'rollback'
  1482. * @param array contains context information about the debug() call
  1483. * common keys are: is_manip, time, result etc.
  1484. *
  1485. * @return void
  1486. *
  1487. * @access public
  1488. */
  1489. function debug($message, $scope = '', $context = array())
  1490. {
  1491. if ($this->options['debug'] && $this->options['debug_handler'])
  1492. {
  1493. if (! $this->options['debug_expanded_output'])
  1494. {
  1495. if (! empty($context['when']) && $context['when'] !== 'pre')
  1496. {
  1497. return null;
  1498. }
  1499. $context = empty($context['is_manip']) ? false : $context['is_manip'];
  1500. }
  1501. return call_user_func_array($this->options['debug_handler'], array(&$this, $scope, $message, $context));
  1502. }
  1503. return null;
  1504. }
  1505. // }}}
  1506. // {{{ function getDebugOutput()
  1507. /**
  1508. * output debug info
  1509. *
  1510. * @return string content of the debug_output class variable
  1511. *
  1512. * @access public
  1513. */
  1514. function getDebugOutput()
  1515. {
  1516. return $this->debug_output;
  1517. }
  1518. // }}}
  1519. // {{{ function escape($text)
  1520. /**
  1521. * Quotes a string so it can be safely used in a query. It will quote
  1522. * the text so it can safely be used within a query.
  1523. *
  1524. * @param string the input string to quote
  1525. * @param bool escape wildcards
  1526. *
  1527. * @return string quoted string
  1528. *
  1529. * @access public
  1530. */
  1531. function escape($text, $escape_wildcards = false)
  1532. {
  1533. if ($escape_wildcards)
  1534. {
  1535. $text = $this->escapePattern($text);
  1536. }
  1537. $text = str_replace($this->string_quoting['end'], $this->string_quoting['escape'] . $this->string_quoting['end'], $text);
  1538. return $text;
  1539. }
  1540. // }}}
  1541. // {{{ function escapePattern($text)
  1542. /**
  1543. * Quotes pattern (% and _) characters in a string)
  1544. *
  1545. * @param string the input string to quote
  1546. *
  1547. * @return string quoted string
  1548. *
  1549. * @access public
  1550. */
  1551. function escapePattern($text)
  1552. {
  1553. if ($this->string_quoting['escape_pattern'])
  1554. {
  1555. $text = str_replace($this->string_quoting['escape_pattern'], $this->string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $text);
  1556. foreach ($this->wildcards as $wildcard)
  1557. {
  1558. $text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $wildcard, $text);
  1559. }
  1560. }
  1561. return $text;
  1562. }
  1563. // }}}
  1564. // {{{ function quoteIdentifier($str, $check_option = false)
  1565. /**
  1566. * Quote a string so it can be safely used as a table or column name
  1567. *
  1568. * Delimiting style depends on which database driver is being used.
  1569. *
  1570. * NOTE: just because you CAN use delimited identifiers doesn't mean
  1571. * you SHOULD use them. In general, they end up causing way more
  1572. * problems than they solve.
  1573. *
  1574. * NOTE: if you have table names containing periods, don't use this method
  1575. * (@see bug #11906)
  1576. *
  1577. * Portability is broken by using the following characters inside
  1578. * delimited identifiers:
  1579. * + backtick (<kbd>`</kbd>) -- due to MySQL
  1580. * + double quote (<kbd>"</kbd>) -- due to Oracle
  1581. * + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
  1582. *
  1583. * Delimited identifiers are known to generally work correctly under
  1584. * the following drivers:
  1585. * + mssql
  1586. * + mysql
  1587. * + mysqli
  1588. * + oci8
  1589. * + pgsql
  1590. * + sqlite
  1591. *
  1592. * InterBase doesn't seem to be able to use delimited identifiers
  1593. * via PHP 4. They work fine under PHP 5.
  1594. *
  1595. * @param string identifier name to be quoted
  1596. * @param bool check the 'quote_identifier' option
  1597. *
  1598. * @return string quoted identifier string
  1599. *
  1600. * @access public
  1601. */
  1602. function quoteIdentifier($str, $check_option = false)
  1603. {
  1604. if ($check_option && ! $this->options['quote_identifier'])
  1605. {
  1606. return $str;
  1607. }
  1608. $str = str_replace($this->identifier_quoting['end'], $this->identifier_quoting['escape'] . $this->identifier_quoting['end'], $str);
  1609. $parts = explode('.', $str);
  1610. foreach (array_keys($parts) as $k)
  1611. {
  1612. $parts[$k] = $this->identifier_quoting['start'] . $parts[$k] . $this->identifier_quoting['end'];
  1613. }
  1614. return implode('.', $parts);
  1615. }
  1616. // }}}
  1617. // {{{ function getAsKeyword()
  1618. /**
  1619. * Gets the string to alias column
  1620. *
  1621. * @return string to use when aliasing a column
  1622. */
  1623. function getAsKeyword()
  1624. {
  1625. return $this->as_keyword;
  1626. }
  1627. // }}}
  1628. // {{{ function getConnection()
  1629. /**
  1630. * Returns a native connection
  1631. *
  1632. * @return mixed a valid MDB2 connection object,
  1633. * or a MDB2 error object on error
  1634. *
  1635. * @access public
  1636. */
  1637. function getConnection()
  1638. {
  1639. $result = $this->connect();
  1640. if (PEAR :: isError($result))
  1641. {
  1642. return $result;
  1643. }
  1644. return $this->connection;
  1645. }
  1646. // }}}
  1647. // {{{ function _fixResultArrayValues(&$row, $mode)
  1648. /**
  1649. * Do all necessary conversions on result arrays to fix DBMS quirks
  1650. *
  1651. * @param array the array to be fixed (passed by reference)
  1652. * @param array bit-wise addition of the required portability modes
  1653. *
  1654. * @return void
  1655. *
  1656. * @access protected
  1657. */
  1658. function _fixResultArrayValues(&$row, $mode)
  1659. {
  1660. switch ($mode)
  1661. {
  1662. case MDB2_PORTABILITY_EMPTY_TO_NULL :
  1663. foreach ($row as $key => $value)
  1664. {
  1665. if ($value === '')
  1666. {
  1667. $row[$key] = null;
  1668. }
  1669. }
  1670. break;
  1671. case MDB2_PORTABILITY_RTRIM :
  1672. foreach ($row as $key => $value)
  1673. {
  1674. if (is_string($value))
  1675. {
  1676. $row[$key] = rtrim($value);
  1677. }
  1678. }
  1679. break;
  1680. case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES :
  1681. $tmp_row = array();
  1682. foreach ($row as $key => $value)
  1683. {
  1684. $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1685. }
  1686. $row = $tmp_row;
  1687. break;
  1688. case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL) :
  1689. foreach ($row as $key => $value)
  1690. {
  1691. if ($value === '')
  1692. {
  1693. $row[$key] = null;
  1694. }
  1695. elseif (is_string($value))
  1696. {
  1697. $row[$key] = rtrim($value);
  1698. }
  1699. }
  1700. break;
  1701. case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) :
  1702. $tmp_row = array();
  1703. foreach ($row as $key => $value)
  1704. {
  1705. if (is_string($value))
  1706. {
  1707. $value = rtrim($value);
  1708. }
  1709. $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1710. }
  1711. $row = $tmp_row;
  1712. break;
  1713. case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) :
  1714. $tmp_row = array();
  1715. foreach ($row as $key => $value)
  1716. {
  1717. if ($value === '')
  1718. {
  1719. $value = null;
  1720. }
  1721. $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1722. }
  1723. $row = $tmp_row;
  1724. break;
  1725. case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) :
  1726. $tmp_row = array();
  1727. foreach ($row as $key => $value)
  1728. {
  1729. if ($value === '')
  1730. {
  1731. $value = null;
  1732. }
  1733. elseif (is_string($value))
  1734. {
  1735. $value = rtrim($value);
  1736. }
  1737. $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1738. }
  1739. $row = $tmp_row;
  1740. break;
  1741. }
  1742. }
  1743. // }}}
  1744. // {{{ function &loadModule($module, $property = null, $phptype_specific = null)
  1745. /**
  1746. * loads a module
  1747. *
  1748. * @param string name of the module that should be loaded
  1749. * (only used for error messages)
  1750. * @param string name of the property into which the class will be loaded
  1751. * @param bool if the class to load for the module is specific to the
  1752. * phptype
  1753. *
  1754. * @return object on success a reference to the given module is returned
  1755. * and on failure a PEAR error
  1756. *
  1757. * @access public
  1758. */
  1759. function &loadModule($module, $property = null, $phptype_specific = null)
  1760. {
  1761. if (! $property)
  1762. {
  1763. $property = strtolower($module);
  1764. }
  1765. if (! isset($this->{$property}))
  1766. {
  1767. $version = $phptype_specific;
  1768. if ($phptype_specific !== false)
  1769. {
  1770. $version = true;
  1771. $class_name = 'MDB2_Driver_' . $module . '_' . $this->phptype;
  1772. $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php';
  1773. }
  1774. if ($phptype_specific === false || (! MDB2 :: classExists($class_name) && ! MDB2 :: fileExists($file_name)))
  1775. {
  1776. $version = false;
  1777. $class_name = 'MDB2_' . $module;
  1778. $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php';
  1779. }
  1780. $err = MDB2 :: loadClass($class_name, $this->getOption('debug'));
  1781. if (PEAR :: isError($err))
  1782. {
  1783. return $err;
  1784. }
  1785. // load module in a specific version
  1786. if ($version)
  1787. {
  1788. if (method_exists($class_name, 'getClassName'))
  1789. {
  1790. $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index);
  1791. if ($class_name != $class_name_new)
  1792. {
  1793. $class_name = $class_name_new;
  1794. $err = MDB2 :: loadClass($class_name, $this->getOption('debug'));
  1795. if (PEAR :: isError($err))
  1796. {
  1797. return $err;
  1798. }
  1799. }
  1800. }
  1801. }
  1802. if (! MDB2 :: classExists($class_name))
  1803. {
  1804. $err = & $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, "unable to load module '$module' into property '$property'", __FUNCTION__);
  1805. return $err;
  1806. }
  1807. $this->{$property} = new $class_name($this->db_index);
  1808. $this->modules[$module] = & $this->{$property};
  1809. if ($version)
  1810. {
  1811. // this will be used in the connect method to determine if the module
  1812. // needs to be loaded with a different version if the server
  1813. // version changed in between connects
  1814. $this->loaded_version_modules[] = $property;
  1815. }
  1816. }
  1817. return $this->{$property};
  1818. }
  1819. // }}}
  1820. // {{{ function __call($method, $params)
  1821. /**
  1822. * Calls a module method using the __call magic method
  1823. *
  1824. * @param string Method name.
  1825. * @param array Arguments.
  1826. *
  1827. * @return mixed Returned value.
  1828. */
  1829. function __call($method, $params)
  1830. {
  1831. $module = null;
  1832. if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match) && isset($this->options['modules'][$match[1]]))
  1833. {
  1834. $module = $this->options['modules'][$match[1]];
  1835. $method = strtolower($match[2]) . $match[3];
  1836. if (! isset($this->modules[$module]) || ! is_object($this->modules[$module]))
  1837. {
  1838. $result = & $this->loadModule($module);
  1839. if (PEAR :: isError($result))
  1840. {
  1841. return $result;
  1842. }
  1843. }
  1844. }
  1845. else
  1846. {
  1847. foreach ($this->modules as $key => $foo)
  1848. {
  1849. if (is_object($this->modules[$key]) && method_exists($this->modules[$key], $method))
  1850. {
  1851. $module = $key;
  1852. break;
  1853. }
  1854. }
  1855. }
  1856. if (! is_null($module))
  1857. {
  1858. return call_user_func_array(array(&$this->modules[$module], $method), $params);
  1859. }
  1860. trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR);
  1861. }
  1862. // }}}
  1863. // {{{ function beginTransaction($savepoint = null)
  1864. /**
  1865. * Start a transaction or set a savepoint.
  1866. *
  1867. * @param string name of a savepoint to set
  1868. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1869. *
  1870. * @access public
  1871. */
  1872. function beginTransaction($savepoint = null)
  1873. {
  1874. $this->debug('Starting transaction', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  1875. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'transactions are not supported', __FUNCTION__);
  1876. }
  1877. // }}}
  1878. // {{{ function commit($savepoint = null)
  1879. /**
  1880. * Commit the database changes done during a transaction that is in
  1881. * progress or release a savepoint. This function may only be called when
  1882. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  1883. * transaction is implicitly started after committing the pending changes.
  1884. *
  1885. * @param string name of a savepoint to release
  1886. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1887. *
  1888. * @access public
  1889. */
  1890. function commit($savepoint = null)
  1891. {
  1892. $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  1893. 'savepoint' => $savepoint));
  1894. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'commiting transactions is not supported', __FUNCTION__);
  1895. }
  1896. // }}}
  1897. // {{{ function rollback($savepoint = null)
  1898. /**
  1899. * Cancel any database changes done during a transaction or since a specific
  1900. * savepoint that is in progress. This function may only be called when
  1901. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  1902. * transaction is implicitly started after canceling the pending changes.
  1903. *
  1904. * @param string name of a savepoint to rollback to
  1905. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1906. *
  1907. * @access public
  1908. */
  1909. function rollback($savepoint = null)
  1910. {
  1911. $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true,
  1912. 'savepoint' => $savepoint));
  1913. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'rolling back transactions is not supported', __FUNCTION__);
  1914. }
  1915. // }}}
  1916. // {{{ function inTransaction($ignore_nested = false)
  1917. /**
  1918. * If a transaction is currently open.
  1919. *
  1920. * @param bool if the nested transaction count should be ignored
  1921. * @return int|bool - an integer with the nesting depth is returned if a
  1922. * nested transaction is open
  1923. * - true is returned for a normal open transaction
  1924. * - false is returned if no transaction is open
  1925. *
  1926. * @access public
  1927. */
  1928. function inTransaction($ignore_nested = false)
  1929. {
  1930. if (! $ignore_nested && isset($this->nested_transaction_counter))
  1931. {
  1932. return $this->nested_transaction_counter;
  1933. }
  1934. return $this->in_transaction;
  1935. }
  1936. // }}}
  1937. // {{{ function setTransactionIsolation($isolation)
  1938. /**
  1939. * Set the transacton isolation level.
  1940. *
  1941. * @param string standard isolation level
  1942. * READ UNCOMMITTED (allows dirty reads)
  1943. * READ COMMITTED (prevents dirty reads)
  1944. * REPEATABLE READ (prevents nonrepeatable reads)
  1945. * SERIALIZABLE (prevents phantom reads)
  1946. * @param array some transaction options:
  1947. * 'wait' => 'WAIT' | 'NO WAIT'
  1948. * 'rw' => 'READ WRITE' | 'READ ONLY'
  1949. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1950. *
  1951. * @access public
  1952. * @since 2.1.1
  1953. */
  1954. function setTransactionIsolation($isolation, $options = array())
  1955. {
  1956. $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
  1957. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'isolation level setting is not supported', __FUNCTION__);
  1958. }
  1959. // }}}
  1960. // {{{ function beginNestedTransaction($savepoint = false)
  1961. /**
  1962. * Start a nested transaction.
  1963. *
  1964. * @return mixed MDB2_OK on success/savepoint name, a MDB2 error on failure
  1965. *
  1966. * @access public
  1967. * @since 2.1.1
  1968. */
  1969. function beginNestedTransaction()
  1970. {
  1971. if ($this->in_transaction)
  1972. {
  1973. ++ $this->nested_transaction_counter;
  1974. $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
  1975. if ($this->supports('savepoints') && $savepoint)
  1976. {
  1977. return $this->beginTransaction($savepoint);
  1978. }
  1979. return MDB2_OK;
  1980. }
  1981. $this->has_transaction_error = false;
  1982. $result = $this->beginTransaction();
  1983. $this->nested_transaction_counter = 1;
  1984. return $result;
  1985. }
  1986. // }}}
  1987. // {{{ function completeNestedTransaction($force_rollback = false, $release = false)
  1988. /**
  1989. * Finish a nested transaction by rolling back if an error occured or
  1990. * committing otherwise.
  1991. *
  1992. * @param bool if the transaction should be rolled back regardless
  1993. * even if no error was set within the nested transaction
  1994. * @return mixed MDB_OK on commit/counter decrementing, false on rollback
  1995. * and a MDB2 error on failure
  1996. *
  1997. * @access public
  1998. * @since 2.1.1
  1999. */
  2000. function completeNestedTransaction($force_rollback = false)
  2001. {
  2002. if ($this->nested_transaction_counter > 1)
  2003. {
  2004. $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
  2005. if ($this->supports('savepoints') && $savepoint)
  2006. {
  2007. if ($force_rollback || $this->has_transaction_error)
  2008. {
  2009. $result = $this->rollback($savepoint);
  2010. if (! PEAR :: isError($result))
  2011. {
  2012. $result = false;
  2013. $this->has_transaction_error = false;
  2014. }
  2015. }
  2016. else
  2017. {
  2018. $result = $this->commit($savepoint);
  2019. }
  2020. }
  2021. else
  2022. {
  2023. $result = MDB2_OK;
  2024. }
  2025. -- $this->nested_transaction_counter;
  2026. return $result;
  2027. }
  2028. $this->nested_transaction_counter = null;
  2029. $result = MDB2_OK;
  2030. // transaction has not yet been rolled back
  2031. if ($this->in_transaction)
  2032. {
  2033. if ($force_rollback || $this->has_transaction_error)
  2034. {
  2035. $result = $this->rollback();
  2036. if (! PEAR :: isError($result))
  2037. {
  2038. $result = false;
  2039. }
  2040. }
  2041. else
  2042. {
  2043. $result = $this->commit();
  2044. }
  2045. }
  2046. $this->has_transaction_error = false;
  2047. return $result;
  2048. }
  2049. // }}}
  2050. // {{{ function failNestedTransaction($error = null, $immediately = false)
  2051. /**
  2052. * Force setting nested transaction to failed.
  2053. *
  2054. * @param mixed value to return in getNestededTransactionError()
  2055. * @param bool if the transaction should be rolled back immediately
  2056. * @return bool MDB2_OK
  2057. *
  2058. * @access public
  2059. * @since 2.1.1
  2060. */
  2061. function failNestedTransaction($error = null, $immediately = false)
  2062. {
  2063. if (is_null($error))
  2064. {
  2065. $error = $this->has_transaction_error ? $this->has_transaction_error : true;
  2066. }
  2067. elseif (! $error)
  2068. {
  2069. $error = true;
  2070. }
  2071. $this->has_transaction_error = $error;
  2072. if (! $immediately)
  2073. {
  2074. return MDB2_OK;
  2075. }
  2076. return $this->rollback();
  2077. }
  2078. // }}}
  2079. // {{{ function getNestedTransactionError()
  2080. /**
  2081. * The first error that occured since the transaction start.
  2082. *
  2083. * @return MDB2_Error|bool MDB2 error object if an error occured or false.
  2084. *
  2085. * @access public
  2086. * @since 2.1.1
  2087. */
  2088. function getNestedTransactionError()
  2089. {
  2090. return $this->has_transaction_error;
  2091. }
  2092. // }}}
  2093. // {{{ connect()
  2094. /**
  2095. * Connect to the database
  2096. *
  2097. * @return true on success, MDB2 Error Object on failure
  2098. */
  2099. function connect()
  2100. {
  2101. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  2102. }
  2103. // }}}
  2104. // {{{ databaseExists()
  2105. /**
  2106. * check if given database name is exists?
  2107. *
  2108. * @param string $name name of the database that should be checked
  2109. *
  2110. * @return mixed true/false on success, a MDB2 error on failure
  2111. * @access public
  2112. */
  2113. function databaseExists($name)
  2114. {
  2115. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  2116. }
  2117. // }}}
  2118. // {{{ setCharset($charset, $connection = null)
  2119. /**
  2120. * Set the charset on the current connection
  2121. *
  2122. * @param string charset
  2123. * @param resource connection handle
  2124. *
  2125. * @return true on success, MDB2 Error Object on failure
  2126. */
  2127. function setCharset($charset, $connection = null)
  2128. {
  2129. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  2130. }
  2131. // }}}
  2132. // {{{ function disconnect($force = true)
  2133. /**
  2134. * Log out and disconnect from the database.
  2135. *
  2136. * @param boolean $force whether the disconnect should be forced even if the
  2137. * connection is opened persistently
  2138. *
  2139. * @return mixed true on success, false if not connected and error object on error
  2140. *
  2141. * @access public
  2142. */
  2143. function disconnect($force = true)
  2144. {
  2145. $this->connection = 0;
  2146. $this->connected_dsn = array();
  2147. $this->connected_database_name = '';
  2148. $this->opened_persistent = null;
  2149. $this->connected_server_info = '';
  2150. $this->in_transaction = null;
  2151. $this->nested_transaction_counter = null;
  2152. return MDB2_OK;
  2153. }
  2154. // }}}
  2155. // {{{ function setDatabase($name)
  2156. /**
  2157. * Select a different database
  2158. *
  2159. * @param string name of the database that should be selected
  2160. *
  2161. * @return string name of the database previously connected to
  2162. *
  2163. * @access public
  2164. */
  2165. function setDatabase($name)
  2166. {
  2167. $previous_database_name = (isset($this->database_name)) ? $this->database_name : '';
  2168. $this->database_name = $name;
  2169. if (! empty($this->connected_database_name) && ($this->connected_database_name != $this->database_name))
  2170. {
  2171. $this->disconnect(false);
  2172. }
  2173. return $previous_database_name;
  2174. }
  2175. // }}}
  2176. // {{{ function getDatabase()
  2177. /**
  2178. * Get the current database
  2179. *
  2180. * @return string name of the database
  2181. *
  2182. * @access public
  2183. */
  2184. function getDatabase()
  2185. {
  2186. return $this->database_name;
  2187. }
  2188. // }}}
  2189. // {{{ function setDSN($dsn)
  2190. /**
  2191. * set the DSN
  2192. *
  2193. * @param mixed DSN string or array
  2194. *
  2195. * @return MDB2_OK
  2196. *
  2197. * @access public
  2198. */
  2199. function setDSN($dsn)
  2200. {
  2201. $dsn_default = $GLOBALS['_MDB2_dsninfo_default'];
  2202. $dsn = MDB2 :: parseDSN($dsn);
  2203. if (array_key_exists('database', $dsn))
  2204. {
  2205. $this->database_name = $dsn['database'];
  2206. unset($dsn['database']);
  2207. }
  2208. $this->dsn = array_merge($dsn_default, $dsn);
  2209. return $this->disconnect(false);
  2210. }
  2211. // }}}
  2212. // {{{ function getDSN($type = 'string', $hidepw = false)
  2213. /**
  2214. * return the DSN as a string
  2215. *
  2216. * @param string format to return ("array", "string")
  2217. * @param string string to hide the password with
  2218. *
  2219. * @return mixed DSN in the chosen type
  2220. *
  2221. * @access public
  2222. */
  2223. function getDSN($type = 'string', $hidepw = false)
  2224. {
  2225. $dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn);
  2226. $dsn['phptype'] = $this->phptype;
  2227. $dsn['database'] = $this->database_name;
  2228. if ($hidepw)
  2229. {
  2230. $dsn['password'] = $hidepw;
  2231. }
  2232. switch ($type)
  2233. {
  2234. // expand to include all possible options
  2235. case 'string' :
  2236. $dsn = $dsn['phptype'] . ($dsn['dbsyntax'] ? ('(' . $dsn['dbsyntax'] . ')') : '') . '://' . $dsn['username'] . ':' . $dsn['password'] . '@' . $dsn['hostspec'] . ($dsn['port'] ? (':' . $dsn['port']) : '') . '/' . $dsn['database'];
  2237. break;
  2238. case 'array' :
  2239. default :
  2240. break;
  2241. }
  2242. return $dsn;
  2243. }
  2244. // }}}
  2245. // {{{ _isNewLinkSet()
  2246. /**
  2247. * Check if the 'new_link' option is set
  2248. *
  2249. * @return boolean
  2250. *
  2251. * @access protected
  2252. */
  2253. function _isNewLinkSet()
  2254. {
  2255. return (isset($this->dsn['new_link']) && ($this->dsn['new_link'] === true || (is_string($this->dsn['new_link']) && preg_match('/^true$/i', $this->dsn['new_link'])) || (is_numeric($this->dsn['new_link']) && 0 != (int) $this->dsn['new_link'])));
  2256. }
  2257. // }}}
  2258. // {{{ function &standaloneQuery($query, $types = null, $is_manip = false)
  2259. /**
  2260. * execute a query as database administrator
  2261. *
  2262. * @param string the SQL query
  2263. * @param mixed array that contains the types of the columns in
  2264. * the result set
  2265. * @param bool if the query is a manipulation query
  2266. *
  2267. * @return mixed MDB2_OK on success, a MDB2 error on failure
  2268. *
  2269. * @access public
  2270. */
  2271. function &standaloneQuery($query, $types = null, $is_manip = false)
  2272. {
  2273. $offset = $this->offset;
  2274. $limit = $this->limit;
  2275. $this->offset = $this->limit = 0;
  2276. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  2277. $connection = $this->getConnection();
  2278. if (PEAR :: isError($connection))
  2279. {
  2280. return $connection;
  2281. }
  2282. $result = & $this->_doQuery($query, $is_manip, $connection, false);
  2283. if (PEAR :: isError($result))
  2284. {
  2285. return $result;
  2286. }
  2287. if ($is_manip)
  2288. {
  2289. $affected_rows = $this->_affectedRows($connection, $result);
  2290. return $affected_rows;
  2291. }
  2292. $result = & $this->_wrapResult($result, $types, true, false, $limit, $offset);
  2293. return $result;
  2294. }
  2295. // }}}
  2296. // {{{ function _modifyQuery($query, $is_manip, $limit, $offset)
  2297. /**
  2298. * Changes a query string for various DBMS specific reasons
  2299. *
  2300. * @param string query to modify
  2301. * @param bool if it is a DML query
  2302. * @param int limit the number of rows
  2303. * @param int start reading from given offset
  2304. *
  2305. * @return string modified query
  2306. *
  2307. * @access protected
  2308. */
  2309. function _modifyQuery($query, $is_manip, $limit, $offset)
  2310. {
  2311. return $query;
  2312. }
  2313. // }}}
  2314. // {{{ function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  2315. /**
  2316. * Execute a query
  2317. * @param string query
  2318. * @param bool if the query is a manipulation query
  2319. * @param resource connection handle
  2320. * @param string database name
  2321. *
  2322. * @return result or error object
  2323. *
  2324. * @access protected
  2325. */
  2326. function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  2327. {
  2328. $this->last_query = $query;
  2329. $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  2330. if ($result)
  2331. {
  2332. if (PEAR :: isError($result))
  2333. {
  2334. return $result;
  2335. }
  2336. $query = $result;
  2337. }
  2338. $err = & $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  2339. return $err;
  2340. }
  2341. // }}}
  2342. // {{{ function _affectedRows($connection, $result = null)
  2343. /**
  2344. * Returns the number of rows affected
  2345. *
  2346. * @param resource result handle
  2347. * @param resource connection handle
  2348. *
  2349. * @return mixed MDB2 Error Object or the number of rows affected
  2350. *
  2351. * @access private
  2352. */
  2353. function _affectedRows($connection, $result = null)
  2354. {
  2355. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  2356. }
  2357. // }}}
  2358. // {{{ function &exec($query)
  2359. /**
  2360. * Execute a manipulation query to the database and return the number of affected rows
  2361. *
  2362. * @param string the SQL query
  2363. *
  2364. * @return mixed number of affected rows on success, a MDB2 error on failure
  2365. *
  2366. * @access public
  2367. */
  2368. function &exec($query)
  2369. {
  2370. $offset = $this->offset;
  2371. $limit = $this->limit;
  2372. $this->offset = $this->limit = 0;
  2373. $query = $this->_modifyQuery($query, true, $limit, $offset);
  2374. $connection = $this->getConnection();
  2375. if (PEAR :: isError($connection))
  2376. {
  2377. return $connection;
  2378. }
  2379. $result = & $this->_doQuery($query, true, $connection, $this->database_name);
  2380. if (PEAR :: isError($result))
  2381. {
  2382. return $result;
  2383. }
  2384. $affectedRows = $this->_affectedRows($connection, $result);
  2385. return $affectedRows;
  2386. }
  2387. // }}}
  2388. // {{{ function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
  2389. /**
  2390. * Send a query to the database and return any results
  2391. *
  2392. * @param string the SQL query
  2393. * @param mixed array that contains the types of the columns in
  2394. * the result set
  2395. * @param mixed string which specifies which result class to use
  2396. * @param mixed string which specifies which class to wrap results in
  2397. *
  2398. * @return mixed an MDB2_Result handle on success, a MDB2 error on failure
  2399. *
  2400. * @access public
  2401. */
  2402. function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
  2403. {
  2404. $offset = $this->offset;
  2405. $limit = $this->limit;
  2406. $this->offset = $this->limit = 0;
  2407. $query = $this->_modifyQuery($query, false, $limit, $offset);
  2408. $connection = $this->getConnection();
  2409. if (PEAR :: isError($connection))
  2410. {
  2411. return $connection;
  2412. }
  2413. $result = & $this->_doQuery($query, false, $connection, $this->database_name);
  2414. if (PEAR :: isError($result))
  2415. {
  2416. return $result;
  2417. }
  2418. $result = & $this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset);
  2419. return $result;
  2420. }
  2421. // }}}
  2422. // {{{ function &_wrapResult($result, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null)
  2423. /**
  2424. * wrap a result set into the correct class
  2425. *
  2426. * @param resource result handle
  2427. * @param mixed array that contains the types of the columns in
  2428. * the result set
  2429. * @param mixed string which specifies which result class to use
  2430. * @param mixed string which specifies which class to wrap results in
  2431. * @param string number of rows to select
  2432. * @param string first row to select
  2433. *
  2434. * @return mixed an MDB2_Result, a MDB2 error on failure
  2435. *
  2436. * @access protected
  2437. */
  2438. function &_wrapResult($result, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null)
  2439. {
  2440. if ($types === true)
  2441. {
  2442. if ($this->supports('result_introspection'))
  2443. {
  2444. $this->loadModule('Reverse', null, true);
  2445. $tableInfo = $this->reverse->tableInfo($result);
  2446. if (PEAR :: isError($tableInfo))
  2447. {
  2448. return $tableInfo;
  2449. }
  2450. $types = array();
  2451. $types_assoc = array();
  2452. foreach ($tableInfo as $field)
  2453. {
  2454. $types[] = $field['mdb2type'];
  2455. $types_assoc[$field['name']] = $field['mdb2type'];
  2456. }
  2457. }
  2458. else
  2459. {
  2460. $types = null;
  2461. }
  2462. }
  2463. if ($result_class === true)
  2464. {
  2465. $result_class = $this->options['result_buffering'] ? $this->options['buffered_result_class'] : $this->options['result_class'];
  2466. }
  2467. if ($result_class)
  2468. {
  2469. $class_name = sprintf($result_class, $this->phptype);
  2470. if (! MDB2 :: classExists($class_name))
  2471. {
  2472. $err = & $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class does not exist ' . $class_name, __FUNCTION__);
  2473. return $err;
  2474. }
  2475. $reference_result = new $class_name($this, $result, $limit, $offset);
  2476. $result = &$reference_result;
  2477. if (! MDB2 :: isResultCommon($result))
  2478. {
  2479. $err = & $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class is not extended from MDB2_Result_Common', __FUNCTION__);
  2480. return $err;
  2481. }
  2482. if (! empty($types))
  2483. {
  2484. $err = $result->setResultTypes($types);
  2485. if (PEAR :: isError($err))
  2486. {
  2487. $result->free();
  2488. return $err;
  2489. }
  2490. }
  2491. if (! empty($types_assoc))
  2492. {
  2493. $err = $result->setResultTypesAssoc($types_assoc);
  2494. if (PEAR :: isError($err))
  2495. {
  2496. $result->free();
  2497. return $err;
  2498. }
  2499. }
  2500. }
  2501. if ($result_wrap_class === true)
  2502. {
  2503. $result_wrap_class = $this->options['result_wrap_class'];
  2504. }
  2505. if ($result_wrap_class)
  2506. {
  2507. if (! MDB2 :: classExists($result_wrap_class))
  2508. {
  2509. $err = & $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result wrap class does not exist ' . $result_wrap_class, __FUNCTION__);
  2510. return $err;
  2511. }
  2512. $result = new $result_wrap_class($result, $this->fetchmode);
  2513. }
  2514. return $result;
  2515. }
  2516. // }}}
  2517. // {{{ function getServerVersion($native = false)
  2518. /**
  2519. * return version information about the server
  2520. *
  2521. * @param bool determines if the raw version string should be returned
  2522. *
  2523. * @return mixed array with version information or row string
  2524. *
  2525. * @access public
  2526. */
  2527. function getServerVersion($native = false)
  2528. {
  2529. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  2530. }
  2531. // }}}
  2532. // {{{ function setLimit($limit, $offset = null)
  2533. /**
  2534. * set the range of the next query
  2535. *
  2536. * @param string number of rows to select
  2537. * @param string first row to select
  2538. *
  2539. * @return mixed MDB2_OK on success, a MDB2 error on failure
  2540. *
  2541. * @access public
  2542. */
  2543. function setLimit($limit, $offset = null)
  2544. {
  2545. if (! $this->supports('limit_queries'))
  2546. {
  2547. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'limit is not supported by this driver', __FUNCTION__);
  2548. }
  2549. $limit = (int) $limit;
  2550. if ($limit < 0)
  2551. {
  2552. return $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid selected range row limit', __FUNCTION__);
  2553. }
  2554. $this->limit = $limit;
  2555. if (! is_null($offset))
  2556. {
  2557. $offset = (int) $offset;
  2558. if ($offset < 0)
  2559. {
  2560. return $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid first selected range row', __FUNCTION__);
  2561. }
  2562. $this->offset = $offset;
  2563. }
  2564. return MDB2_OK;
  2565. }
  2566. // }}}
  2567. // {{{ function subSelect($query, $type = false)
  2568. /**
  2569. * simple subselect emulation: leaves the query untouched for all RDBMS
  2570. * that support subselects
  2571. *
  2572. * @param string the SQL query for the subselect that may only
  2573. * return a column
  2574. * @param string determines type of the field
  2575. *
  2576. * @return string the query
  2577. *
  2578. * @access public
  2579. */
  2580. function subSelect($query, $type = false)
  2581. {
  2582. if ($this->supports('sub_selects') === true)
  2583. {
  2584. return $query;
  2585. }
  2586. if (! $this->supports('sub_selects'))
  2587. {
  2588. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  2589. }
  2590. $col = $this->queryCol($query, $type);
  2591. if (PEAR :: isError($col))
  2592. {
  2593. return $col;
  2594. }
  2595. if (! is_array($col) || count($col) == 0)
  2596. {
  2597. return 'NULL';
  2598. }
  2599. if ($type)
  2600. {
  2601. $this->loadModule('Datatype', null, true);
  2602. return $this->datatype->implodeArray($col, $type);
  2603. }
  2604. return implode(', ', $col);
  2605. }
  2606. // }}}
  2607. // {{{ function replace($table, $fields)
  2608. /**
  2609. * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
  2610. * query, except that if there is already a row in the table with the same
  2611. * key field values, the old row is deleted before the new row is inserted.
  2612. *
  2613. * The REPLACE type of query does not make part of the SQL standards. Since
  2614. * practically only MySQL and SQLite implement it natively, this type of
  2615. * query isemulated through this method for other DBMS using standard types
  2616. * of queries inside a transaction to assure the atomicity of the operation.
  2617. *
  2618. * @param string name of the table on which the REPLACE query will
  2619. * be executed.
  2620. * @param array associative array that describes the fields and the
  2621. * values that will be inserted or updated in the specified table. The
  2622. * indexes of the array are the names of all the fields of the table.
  2623. * The values of the array are also associative arrays that describe
  2624. * the values and other properties of the table fields.
  2625. *
  2626. * Here follows a list of field properties that need to be specified:
  2627. *
  2628. * value
  2629. * Value to be assigned to the specified field. This value may be
  2630. * of specified in database independent type format as this
  2631. * function can perform the necessary datatype conversions.
  2632. *
  2633. * Default: this property is required unless the Null property is
  2634. * set to 1.
  2635. *
  2636. * type
  2637. * Name of the type of the field. Currently, all types MDB2
  2638. * are supported except for clob and blob.
  2639. *
  2640. * Default: no type conversion
  2641. *
  2642. * null
  2643. * bool property that indicates that the value for this field
  2644. * should be set to null.
  2645. *
  2646. * The default value for fields missing in INSERT queries may be
  2647. * specified the definition of a table. Often, the default value
  2648. * is already null, but since the REPLACE may be emulated using
  2649. * an UPDATE query, make sure that all fields of the table are
  2650. * listed in this function argument array.
  2651. *
  2652. * Default: 0
  2653. *
  2654. * key
  2655. * bool property that indicates that this field should be
  2656. * handled as a primary key or at least as part of the compound
  2657. * unique index of the table that will determine the row that will
  2658. * updated if it exists or inserted a new row otherwise.
  2659. *
  2660. * This function will fail if no key field is specified or if the
  2661. * value of a key field is set to null because fields that are
  2662. * part of unique index they may not be null.
  2663. *
  2664. * Default: 0
  2665. *
  2666. * @return mixed MDB2_OK on success, a MDB2 error on failure
  2667. *
  2668. * @access public
  2669. */
  2670. function replace($table, $fields)
  2671. {
  2672. if (! $this->supports('replace'))
  2673. {
  2674. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'replace query is not supported', __FUNCTION__);
  2675. }
  2676. $count = count($fields);
  2677. $condition = $values = array();
  2678. for($colnum = 0, reset($fields); $colnum < $count; next($fields), $colnum ++)
  2679. {
  2680. $name = key($fields);
  2681. if (isset($fields[$name]['null']) && $fields[$name]['null'])
  2682. {
  2683. $value = 'NULL';
  2684. }
  2685. else
  2686. {
  2687. $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
  2688. $value = $this->quote($fields[$name]['value'], $type);
  2689. }
  2690. $values[$name] = $value;
  2691. if (isset($fields[$name]['key']) && $fields[$name]['key'])
  2692. {
  2693. if ($value === 'NULL')
  2694. {
  2695. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'key value ' . $name . ' may not be NULL', __FUNCTION__);
  2696. }
  2697. $condition[] = $this->quoteIdentifier($name, true) . '=' . $value;
  2698. }
  2699. }
  2700. if (empty($condition))
  2701. {
  2702. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'not specified which fields are keys', __FUNCTION__);
  2703. }
  2704. $result = null;
  2705. $in_transaction = $this->in_transaction;
  2706. if (! $in_transaction && PEAR :: isError($result = $this->beginTransaction()))
  2707. {
  2708. return $result;
  2709. }
  2710. $connection = $this->getConnection();
  2711. if (PEAR :: isError($connection))
  2712. {
  2713. return $connection;
  2714. }
  2715. $condition = ' WHERE ' . implode(' AND ', $condition);
  2716. $query = 'DELETE FROM ' . $this->quoteIdentifier($table, true) . $condition;
  2717. $result = & $this->_doQuery($query, true, $connection);
  2718. if (! PEAR :: isError($result))
  2719. {
  2720. $affected_rows = $this->_affectedRows($connection, $result);
  2721. $insert = '';
  2722. foreach ($values as $key => $value)
  2723. {
  2724. $insert .= ($insert ? ', ' : '') . $this->quoteIdentifier($key, true);
  2725. }
  2726. $values = implode(', ', $values);
  2727. $query = 'INSERT INTO ' . $this->quoteIdentifier($table, true) . "($insert) VALUES ($values)";
  2728. $result = & $this->_doQuery($query, true, $connection);
  2729. if (! PEAR :: isError($result))
  2730. {
  2731. $affected_rows += $this->_affectedRows($connection, $result);
  2732. ;
  2733. }
  2734. }
  2735. if (! $in_transaction)
  2736. {
  2737. if (PEAR :: isError($result))
  2738. {
  2739. $this->rollback();
  2740. }
  2741. else
  2742. {
  2743. $result = $this->commit();
  2744. }
  2745. }
  2746. if (PEAR :: isError($result))
  2747. {
  2748. return $result;
  2749. }
  2750. return $affected_rows;
  2751. }
  2752. // }}}
  2753. // {{{ function &prepare($query, $types = null, $result_types = null, $lobs = array())
  2754. /**
  2755. * Prepares a query for multiple execution with execute().
  2756. * With some database backends, this is emulated.
  2757. * prepare() requires a generic query as string like
  2758. * 'INSERT INTO numbers VALUES(?,?)' or
  2759. * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  2760. * The ? and :name and are placeholders which can be set using
  2761. * bindParam() and the query can be sent off using the execute() method.
  2762. * The allowed format for :name can be set with the 'bindname_format' option.
  2763. *
  2764. * @param string the query to prepare
  2765. * @param mixed array that contains the types of the placeholders
  2766. * @param mixed array that contains the types of the columns in
  2767. * the result set or MDB2_PREPARE_RESULT, if set to
  2768. * MDB2_PREPARE_MANIP the query is handled as a manipulation query
  2769. * @param mixed key (field) value (parameter) pair for all lob placeholders
  2770. *
  2771. * @return mixed resource handle for the prepared query on success,
  2772. * a MDB2 error on failure
  2773. *
  2774. * @access public
  2775. * @see bindParam, execute
  2776. */
  2777. function &prepare($query, $types = null, $result_types = null, $lobs = array())
  2778. {
  2779. $is_manip = ($result_types === MDB2_PREPARE_MANIP);
  2780. $offset = $this->offset;
  2781. $limit = $this->limit;
  2782. $this->offset = $this->limit = 0;
  2783. $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
  2784. if ($result)
  2785. {
  2786. if (PEAR :: isError($result))
  2787. {
  2788. return $result;
  2789. }
  2790. $query = $result;
  2791. }
  2792. $placeholder_type_guess = $placeholder_type = null;
  2793. $question = '?';
  2794. $colon = ':';
  2795. $positions = array();
  2796. $position = 0;
  2797. while ($position < strlen($query))
  2798. {
  2799. $q_position = strpos($query, $question, $position);
  2800. $c_position = strpos($query, $colon, $position);
  2801. if ($q_position && $c_position)
  2802. {
  2803. $p_position = min($q_position, $c_position);
  2804. }
  2805. elseif ($q_position)
  2806. {
  2807. $p_position = $q_position;
  2808. }
  2809. elseif ($c_position)
  2810. {
  2811. $p_position = $c_position;
  2812. }
  2813. else
  2814. {
  2815. break;
  2816. }
  2817. if (is_null($placeholder_type))
  2818. {
  2819. $placeholder_type_guess = $query[$p_position];
  2820. }
  2821. $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
  2822. if (PEAR :: isError($new_pos))
  2823. {
  2824. return $new_pos;
  2825. }
  2826. if ($new_pos != $position)
  2827. {
  2828. $position = $new_pos;
  2829. continue; //evaluate again starting from the new position
  2830. }
  2831. if ($query[$position] == $placeholder_type_guess)
  2832. {
  2833. if (is_null($placeholder_type))
  2834. {
  2835. $placeholder_type = $query[$p_position];
  2836. $question = $colon = $placeholder_type;
  2837. if (! empty($types) && is_array($types))
  2838. {
  2839. if ($placeholder_type == ':')
  2840. {
  2841. if (is_int(key($types)))
  2842. {
  2843. $types_tmp = $types;
  2844. $types = array();
  2845. $count = - 1;
  2846. }
  2847. }
  2848. else
  2849. {
  2850. $types = array_values($types);
  2851. }
  2852. }
  2853. }
  2854. if ($placeholder_type == ':')
  2855. {
  2856. $regexp = '/^.{' . ($position + 1) . '}(' . $this->options['bindname_format'] . ').*$/s';
  2857. $parameter = preg_replace($regexp, '\\1', $query);
  2858. if ($parameter === '')
  2859. {
  2860. $err = & $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'named parameter name must match "bindname_format" option', __FUNCTION__);
  2861. return $err;
  2862. }
  2863. $positions[$p_position] = $parameter;
  2864. $query = substr_replace($query, '?', $position, strlen($parameter) + 1);
  2865. // use parameter name in type array
  2866. if (isset($count) && isset($types_tmp[++ $count]))
  2867. {
  2868. $types[$parameter] = $types_tmp[$count];
  2869. }
  2870. }
  2871. else
  2872. {
  2873. $positions[$p_position] = count($positions);
  2874. }
  2875. $position = $p_position + 1;
  2876. }
  2877. else
  2878. {
  2879. $position = $p_position;
  2880. }
  2881. }
  2882. $class_name = 'MDB2_Statement_' . $this->phptype;
  2883. $statement = null;
  2884. $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  2885. $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
  2886. return $obj;
  2887. }
  2888. // }}}
  2889. // {{{ function _skipDelimitedStrings($query, $position, $p_position)
  2890. /**
  2891. * Utility method, used by prepare() to avoid replacing placeholders within delimited strings.
  2892. * Check if the placeholder is contained within a delimited string.
  2893. * If so, skip it and advance the position, otherwise return the current position,
  2894. * which is valid
  2895. *
  2896. * @param string $query
  2897. * @param integer $position current string cursor position
  2898. * @param integer $p_position placeholder position
  2899. *
  2900. * @return mixed integer $new_position on success
  2901. * MDB2_Error on failure
  2902. *
  2903. * @access protected
  2904. */
  2905. function _skipDelimitedStrings($query, $position, $p_position)
  2906. {
  2907. $ignores = $this->string_quoting;
  2908. $ignores[] = $this->identifier_quoting;
  2909. $ignores = array_merge($ignores, $this->sql_comments);
  2910. foreach ($ignores as $ignore)
  2911. {
  2912. if (! empty($ignore['start']))
  2913. {
  2914. if (is_int($start_quote = strpos($query, $ignore['start'], $position)) && $start_quote < $p_position)
  2915. {
  2916. $end_quote = $start_quote;
  2917. do
  2918. {
  2919. if (! is_int($end_quote = strpos($query, $ignore['end'], $end_quote + 1)))
  2920. {
  2921. if ($ignore['end'] === "\n")
  2922. {
  2923. $end_quote = strlen($query) - 1;
  2924. }
  2925. else
  2926. {
  2927. $err = & $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'query with an unterminated text string specified', __FUNCTION__);
  2928. return $err;
  2929. }
  2930. }
  2931. }
  2932. while ($ignore['escape'] && $end_quote - 1 != $start_quote && $query[($end_quote - 1)] == $ignore['escape'] && ($ignore['escape_pattern'] !== $ignore['escape'] || $query[($end_quote - 2)] != $ignore['escape']));
  2933. $position = $end_quote + 1;
  2934. return $position;
  2935. }
  2936. }
  2937. }
  2938. return $position;
  2939. }
  2940. // }}}
  2941. // {{{ function quote($value, $type = null, $quote = true)
  2942. /**
  2943. * Convert a text value into a DBMS specific format that is suitable to
  2944. * compose query statements.
  2945. *
  2946. * @param string text string value that is intended to be converted.
  2947. * @param string type to which the value should be converted to
  2948. * @param bool quote
  2949. * @param bool escape wildcards
  2950. *
  2951. * @return string text string that represents the given argument value in
  2952. * a DBMS specific format.
  2953. *
  2954. * @access public
  2955. */
  2956. function quote($value, $type = null, $quote = true, $escape_wildcards = false)
  2957. {
  2958. $result = $this->loadModule('Datatype', null, true);
  2959. if (PEAR :: isError($result))
  2960. {
  2961. return $result;
  2962. }
  2963. return $this->datatype->quote($value, $type, $quote, $escape_wildcards);
  2964. }
  2965. // }}}
  2966. // {{{ function getDeclaration($type, $name, $field)
  2967. /**
  2968. * Obtain DBMS specific SQL code portion needed to declare
  2969. * of the given type
  2970. *
  2971. * @param string type to which the value should be converted to
  2972. * @param string name the field to be declared.
  2973. * @param string definition of the field
  2974. *
  2975. * @return string DBMS specific SQL code portion that should be used to
  2976. * declare the specified field.
  2977. *
  2978. * @access public
  2979. */
  2980. function getDeclaration($type, $name, $field)
  2981. {
  2982. $result = $this->loadModule('Datatype', null, true);
  2983. if (PEAR :: isError($result))
  2984. {
  2985. return $result;
  2986. }
  2987. return $this->datatype->getDeclaration($type, $name, $field);
  2988. }
  2989. // }}}
  2990. // {{{ function compareDefinition($current, $previous)
  2991. /**
  2992. * Obtain an array of changes that may need to applied
  2993. *
  2994. * @param array new definition
  2995. * @param array old definition
  2996. *
  2997. * @return array containing all changes that will need to be applied
  2998. *
  2999. * @access public
  3000. */
  3001. function compareDefinition($current, $previous)
  3002. {
  3003. $result = $this->loadModule('Datatype', null, true);
  3004. if (PEAR :: isError($result))
  3005. {
  3006. return $result;
  3007. }
  3008. return $this->datatype->compareDefinition($current, $previous);
  3009. }
  3010. // }}}
  3011. // {{{ function supports($feature)
  3012. /**
  3013. * Tell whether a DB implementation or its backend extension
  3014. * supports a given feature.
  3015. *
  3016. * @param string name of the feature (see the MDB2 class doc)
  3017. *
  3018. * @return bool|string if this DB implementation supports a given feature
  3019. * false means no, true means native,
  3020. * 'emulated' means emulated
  3021. *
  3022. * @access public
  3023. */
  3024. function supports($feature)
  3025. {
  3026. if (array_key_exists($feature, $this->supported))
  3027. {
  3028. return $this->supported[$feature];
  3029. }
  3030. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown support feature $feature", __FUNCTION__);
  3031. }
  3032. // }}}
  3033. // {{{ function getSequenceName($sqn)
  3034. /**
  3035. * adds sequence name formatting to a sequence name
  3036. *
  3037. * @param string name of the sequence
  3038. *
  3039. * @return string formatted sequence name
  3040. *
  3041. * @access public
  3042. */
  3043. function getSequenceName($sqn)
  3044. {
  3045. return sprintf($this->options['seqname_format'], preg_replace('/[^a-z0-9_\-\$.]/i', '_', $sqn));
  3046. }
  3047. // }}}
  3048. // {{{ function getIndexName($idx)
  3049. /**
  3050. * adds index name formatting to a index name
  3051. *
  3052. * @param string name of the index
  3053. *
  3054. * @return string formatted index name
  3055. *
  3056. * @access public
  3057. */
  3058. function getIndexName($idx)
  3059. {
  3060. return sprintf($this->options['idxname_format'], preg_replace('/[^a-z0-9_\-\$.]/i', '_', $idx));
  3061. }
  3062. // }}}
  3063. // {{{ function nextID($seq_name, $ondemand = true)
  3064. /**
  3065. * Returns the next free id of a sequence
  3066. *
  3067. * @param string name of the sequence
  3068. * @param bool when true missing sequences are automatic created
  3069. *
  3070. * @return mixed MDB2 Error Object or id
  3071. *
  3072. * @access public
  3073. */
  3074. function nextID($seq_name, $ondemand = true)
  3075. {
  3076. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  3077. }
  3078. // }}}
  3079. // {{{ function lastInsertID($table = null, $field = null)
  3080. /**
  3081. * Returns the autoincrement ID if supported or $id or fetches the current
  3082. * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  3083. *
  3084. * @param string name of the table into which a new row was inserted
  3085. * @param string name of the field into which a new row was inserted
  3086. *
  3087. * @return mixed MDB2 Error Object or id
  3088. *
  3089. * @access public
  3090. */
  3091. function lastInsertID($table = null, $field = null)
  3092. {
  3093. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  3094. }
  3095. // }}}
  3096. // {{{ function currID($seq_name)
  3097. /**
  3098. * Returns the current id of a sequence
  3099. *
  3100. * @param string name of the sequence
  3101. *
  3102. * @return mixed MDB2 Error Object or id
  3103. *
  3104. * @access public
  3105. */
  3106. function currID($seq_name)
  3107. {
  3108. $this->warnings[] = 'database does not support getting current
  3109. sequence value, the sequence value was incremented';
  3110. return $this->nextID($seq_name);
  3111. }
  3112. // }}}
  3113. // {{{ function queryOne($query, $type = null, $colnum = 0)
  3114. /**
  3115. * Execute the specified query, fetch the value from the first column of
  3116. * the first row of the result set and then frees
  3117. * the result set.
  3118. *
  3119. * @param string $query the SELECT query statement to be executed.
  3120. * @param string $type optional argument that specifies the expected
  3121. * datatype of the result set field, so that an eventual
  3122. * conversion may be performed. The default datatype is
  3123. * text, meaning that no conversion is performed
  3124. * @param mixed $colnum the column number (or name) to fetch
  3125. *
  3126. * @return mixed MDB2_OK or field value on success, a MDB2 error on failure
  3127. *
  3128. * @access public
  3129. */
  3130. function queryOne($query, $type = null, $colnum = 0)
  3131. {
  3132. $result = $this->query($query, $type);
  3133. if (! MDB2 :: isResultCommon($result))
  3134. {
  3135. return $result;
  3136. }
  3137. $one = $result->fetchOne($colnum);
  3138. $result->free();
  3139. return $one;
  3140. }
  3141. // }}}
  3142. // {{{ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
  3143. /**
  3144. * Execute the specified query, fetch the values from the first
  3145. * row of the result set into an array and then frees
  3146. * the result set.
  3147. *
  3148. * @param string the SELECT query statement to be executed.
  3149. * @param array optional array argument that specifies a list of
  3150. * expected datatypes of the result set columns, so that the eventual
  3151. * conversions may be performed. The default list of datatypes is
  3152. * empty, meaning that no conversion is performed.
  3153. * @param int how the array data should be indexed
  3154. *
  3155. * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
  3156. *
  3157. * @access public
  3158. */
  3159. function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
  3160. {
  3161. $result = $this->query($query, $types);
  3162. if (! MDB2 :: isResultCommon($result))
  3163. {
  3164. return $result;
  3165. }
  3166. $row = $result->fetchRow($fetchmode);
  3167. $result->free();
  3168. return $row;
  3169. }
  3170. // }}}
  3171. // {{{ function queryCol($query, $type = null, $colnum = 0)
  3172. /**
  3173. * Execute the specified query, fetch the value from the first column of
  3174. * each row of the result set into an array and then frees the result set.
  3175. *
  3176. * @param string $query the SELECT query statement to be executed.
  3177. * @param string $type optional argument that specifies the expected
  3178. * datatype of the result set field, so that an eventual
  3179. * conversion may be performed. The default datatype is text,
  3180. * meaning that no conversion is performed
  3181. * @param mixed $colnum the column number (or name) to fetch
  3182. *
  3183. * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
  3184. * @access public
  3185. */
  3186. function queryCol($query, $type = null, $colnum = 0)
  3187. {
  3188. $result = $this->query($query, $type);
  3189. if (! MDB2 :: isResultCommon($result))
  3190. {
  3191. return $result;
  3192. }
  3193. $col = $result->fetchCol($colnum);
  3194. $result->free();
  3195. return $col;
  3196. }
  3197. // }}}
  3198. // {{{ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
  3199. /**
  3200. * Execute the specified query, fetch all the rows of the result set into
  3201. * a two dimensional array and then frees the result set.
  3202. *
  3203. * @param string the SELECT query statement to be executed.
  3204. * @param array optional array argument that specifies a list of
  3205. * expected datatypes of the result set columns, so that the eventual
  3206. * conversions may be performed. The default list of datatypes is
  3207. * empty, meaning that no conversion is performed.
  3208. * @param int how the array data should be indexed
  3209. * @param bool if set to true, the $all will have the first
  3210. * column as its first dimension
  3211. * @param bool used only when the query returns exactly
  3212. * two columns. If true, the values of the returned array will be
  3213. * one-element arrays instead of scalars.
  3214. * @param bool if true, the values of the returned array is
  3215. * wrapped in another array. If the same key value (in the first
  3216. * column) repeats itself, the values will be appended to this array
  3217. * instead of overwriting the existing values.
  3218. *
  3219. * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
  3220. *
  3221. * @access public
  3222. */
  3223. function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
  3224. {
  3225. $result = $this->query($query, $types);
  3226. if (! MDB2 :: isResultCommon($result))
  3227. {
  3228. return $result;
  3229. }
  3230. $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
  3231. $result->free();
  3232. return $all;
  3233. }
  3234. // }}}
  3235. }
  3236. // }}}
  3237. // {{{ class MDB2_Result
  3238. /**
  3239. * The dummy class that all user space result classes should extend from
  3240. *
  3241. * @package MDB2
  3242. * @category Database
  3243. * @author Lukas Smith <smith@pooteeweet.org>
  3244. */
  3245. class MDB2_Result
  3246. {
  3247. }
  3248. // }}}
  3249. // {{{ class MDB2_Result_Common extends MDB2_Result
  3250. /**
  3251. * The common result class for MDB2 result objects
  3252. *
  3253. * @package MDB2
  3254. * @category Database
  3255. * @author Lukas Smith <smith@pooteeweet.org>
  3256. */
  3257. class MDB2_Result_Common extends MDB2_Result
  3258. {
  3259. // {{{ Variables (Properties)
  3260. var $db;
  3261. var $result;
  3262. var $rownum = - 1;
  3263. var $types = array();
  3264. var $types_assoc = array();
  3265. var $values = array();
  3266. var $offset;
  3267. var $offset_count = 0;
  3268. var $limit;
  3269. var $column_names;
  3270. // }}}
  3271. // {{{ constructor: function __construct(&$db, &$result, $limit = 0, $offset = 0)
  3272. /**
  3273. * Constructor
  3274. */
  3275. function __construct(&$db, &$result, $limit = 0, $offset = 0)
  3276. {
  3277. $this->db = & $db;
  3278. $this->result = & $result;
  3279. $this->offset = $offset;
  3280. $this->limit = max(0, $limit - 1);
  3281. }
  3282. // }}}
  3283. // {{{ function __construct(&$db, &$result, $limit = 0, $offset = 0)
  3284. /**
  3285. * PHP 4 Constructor
  3286. */
  3287. // function __construct(&$db, &$result, $limit = 0, $offset = 0)
  3288. // {
  3289. // $this->__construct($db, $result, $limit, $offset);
  3290. // }
  3291. // }}}
  3292. // {{{ function setResultTypes($types)
  3293. /**
  3294. * Define the list of types to be associated with the columns of a given
  3295. * result set.
  3296. *
  3297. * This function may be called before invoking fetchRow(), fetchOne(),
  3298. * fetchCol() and fetchAll() so that the necessary data type
  3299. * conversions are performed on the data to be retrieved by them. If this
  3300. * function is not called, the type of all result set columns is assumed
  3301. * to be text, thus leading to not perform any conversions.
  3302. *
  3303. * @param array variable that lists the
  3304. * data types to be expected in the result set columns. If this array
  3305. * contains less types than the number of columns that are returned
  3306. * in the result set, the remaining columns are assumed to be of the
  3307. * type text. Currently, the types clob and blob are not fully
  3308. * supported.
  3309. *
  3310. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3311. *
  3312. * @access public
  3313. */
  3314. function setResultTypes($types)
  3315. {
  3316. $load = $this->db->loadModule('Datatype', null, true);
  3317. if (PEAR :: isError($load))
  3318. {
  3319. return $load;
  3320. }
  3321. $types = $this->db->datatype->checkResultTypes($types);
  3322. if (PEAR :: isError($types))
  3323. {
  3324. return $types;
  3325. }
  3326. $this->types = $types;
  3327. return MDB2_OK;
  3328. }
  3329. // {{{ function setResultTypesAssoc($types)
  3330. /**
  3331. * Define the list of types to be associated with the columns of a given
  3332. * result set, when retrieved as an associative array.
  3333. *
  3334. * This function may be called before invoking fetchRow(), fetchOne(),
  3335. * fetchCol() and fetchAll() so that the necessary data type
  3336. * conversions are performed on the data to be retrieved by them. If this
  3337. * function is not called, the type of all result set columns is assumed
  3338. * to be text, thus leading to not perform any conversions.
  3339. *
  3340. * @param array variable that lists the
  3341. * data types to be expected in the result set columns. If this array
  3342. * contains less types than the number of columns that are returned
  3343. * in the result set, the remaining columns are assumed to be of the
  3344. * type text. Currently, the types clob and blob are not fully
  3345. * supported.
  3346. *
  3347. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3348. *
  3349. * @access public
  3350. */
  3351. function setResultTypesAssoc($types)
  3352. {
  3353. $load = $this->db->loadModule('Datatype', null, true);
  3354. if (PEAR :: isError($load))
  3355. {
  3356. return $load;
  3357. }
  3358. $types = $this->db->datatype->checkResultTypes($types);
  3359. if (PEAR :: isError($types))
  3360. {
  3361. return $types;
  3362. }
  3363. $this->types_assoc = $types;
  3364. return MDB2_OK;
  3365. }
  3366. // }}}
  3367. // {{{ function seek($rownum = 0)
  3368. /**
  3369. * Seek to a specific row in a result set
  3370. *
  3371. * @param int number of the row where the data can be found
  3372. *
  3373. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3374. *
  3375. * @access public
  3376. */
  3377. function seek($rownum = 0)
  3378. {
  3379. $target_rownum = $rownum - 1;
  3380. if ($this->rownum > $target_rownum)
  3381. {
  3382. return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'seeking to previous rows not implemented', __FUNCTION__);
  3383. }
  3384. while ($this->rownum < $target_rownum)
  3385. {
  3386. $this->fetchRow();
  3387. }
  3388. return MDB2_OK;
  3389. }
  3390. // }}}
  3391. // {{{ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  3392. /**
  3393. * Fetch and return a row of data
  3394. *
  3395. * @param int how the array data should be indexed
  3396. * @param int number of the row where the data can be found
  3397. *
  3398. * @return int data array on success, a MDB2 error on failure
  3399. *
  3400. * @access public
  3401. */
  3402. function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  3403. {
  3404. $err = & $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  3405. return $err;
  3406. }
  3407. // }}}
  3408. // {{{ function fetchOne($colnum = 0)
  3409. /**
  3410. * fetch single column from the next row from a result set
  3411. *
  3412. * @param int|string the column number (or name) to fetch
  3413. * @param int number of the row where the data can be found
  3414. *
  3415. * @return string data on success, a MDB2 error on failure
  3416. * @access public
  3417. */
  3418. function fetchOne($colnum = 0, $rownum = null)
  3419. {
  3420. $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
  3421. $row = $this->fetchRow($fetchmode, $rownum);
  3422. if (! is_array($row) || PEAR :: isError($row))
  3423. {
  3424. return $row;
  3425. }
  3426. if (! array_key_exists($colnum, $row))
  3427. {
  3428. return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: ' . $colnum, __FUNCTION__);
  3429. }
  3430. return $row[$colnum];
  3431. }
  3432. // }}}
  3433. // {{{ function fetchCol($colnum = 0)
  3434. /**
  3435. * Fetch and return a column from the current row pointer position
  3436. *
  3437. * @param int|string the column number (or name) to fetch
  3438. *
  3439. * @return mixed data array on success, a MDB2 error on failure
  3440. * @access public
  3441. */
  3442. function fetchCol($colnum = 0)
  3443. {
  3444. $column = array();
  3445. $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
  3446. $row = $this->fetchRow($fetchmode);
  3447. if (is_array($row))
  3448. {
  3449. if (! array_key_exists($colnum, $row))
  3450. {
  3451. return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: ' . $colnum, __FUNCTION__);
  3452. }
  3453. do
  3454. {
  3455. $column[] = $row[$colnum];
  3456. }
  3457. while (is_array($row = $this->fetchRow($fetchmode)));
  3458. }
  3459. if (PEAR :: isError($row))
  3460. {
  3461. return $row;
  3462. }
  3463. return $column;
  3464. }
  3465. // }}}
  3466. // {{{ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
  3467. /**
  3468. * Fetch and return all rows from the current row pointer position
  3469. *
  3470. * @param int $fetchmode the fetch mode to use:
  3471. * + MDB2_FETCHMODE_ORDERED
  3472. * + MDB2_FETCHMODE_ASSOC
  3473. * + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIPPED
  3474. * + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPED
  3475. * @param bool if set to true, the $all will have the first
  3476. * column as its first dimension
  3477. * @param bool used only when the query returns exactly
  3478. * two columns. If true, the values of the returned array will be
  3479. * one-element arrays instead of scalars.
  3480. * @param bool if true, the values of the returned array is
  3481. * wrapped in another array. If the same key value (in the first
  3482. * column) repeats itself, the values will be appended to this array
  3483. * instead of overwriting the existing values.
  3484. *
  3485. * @return mixed data array on success, a MDB2 error on failure
  3486. *
  3487. * @access public
  3488. * @see getAssoc()
  3489. */
  3490. function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
  3491. {
  3492. $all = array();
  3493. $row = $this->fetchRow($fetchmode);
  3494. if (PEAR :: isError($row))
  3495. {
  3496. return $row;
  3497. }
  3498. elseif (! $row)
  3499. {
  3500. return $all;
  3501. }
  3502. $shift_array = $rekey ? false : null;
  3503. if (! is_null($shift_array))
  3504. {
  3505. if (is_object($row))
  3506. {
  3507. $colnum = count(get_object_vars($row));
  3508. }
  3509. else
  3510. {
  3511. $colnum = count($row);
  3512. }
  3513. if ($colnum < 2)
  3514. {
  3515. return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, 'rekey feature requires atleast 2 column', __FUNCTION__);
  3516. }
  3517. $shift_array = (! $force_array && $colnum == 2);
  3518. }
  3519. if ($rekey)
  3520. {
  3521. do
  3522. {
  3523. if (is_object($row))
  3524. {
  3525. $arr = get_object_vars($row);
  3526. $key = reset($arr);
  3527. unset($row->{$key});
  3528. }
  3529. else
  3530. {
  3531. if ($fetchmode & MDB2_FETCHMODE_ASSOC)
  3532. {
  3533. $key = reset($row);
  3534. unset($row[key($row)]);
  3535. }
  3536. else
  3537. {
  3538. $key = array_shift($row);
  3539. }
  3540. if ($shift_array)
  3541. {
  3542. $row = array_shift($row);
  3543. }
  3544. }
  3545. if ($group)
  3546. {
  3547. $all[$key][] = $row;
  3548. }
  3549. else
  3550. {
  3551. $all[$key] = $row;
  3552. }
  3553. }
  3554. while (($row = $this->fetchRow($fetchmode)));
  3555. }
  3556. elseif ($fetchmode & MDB2_FETCHMODE_FLIPPED)
  3557. {
  3558. do
  3559. {
  3560. foreach ($row as $key => $val)
  3561. {
  3562. $all[$key][] = $val;
  3563. }
  3564. }
  3565. while (($row = $this->fetchRow($fetchmode)));
  3566. }
  3567. else
  3568. {
  3569. do
  3570. {
  3571. $all[] = $row;
  3572. }
  3573. while (($row = $this->fetchRow($fetchmode)));
  3574. }
  3575. return $all;
  3576. }
  3577. // }}}
  3578. // {{{ function rowCount()
  3579. /**
  3580. * Returns the actual row number that was last fetched (count from 0)
  3581. * @return int
  3582. *
  3583. * @access public
  3584. */
  3585. function rowCount()
  3586. {
  3587. return $this->rownum + 1;
  3588. }
  3589. // }}}
  3590. // {{{ function numRows()
  3591. /**
  3592. * Returns the number of rows in a result object
  3593. *
  3594. * @return mixed MDB2 Error Object or the number of rows
  3595. *
  3596. * @access public
  3597. */
  3598. function numRows()
  3599. {
  3600. return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  3601. }
  3602. // }}}
  3603. // {{{ function nextResult()
  3604. /**
  3605. * Move the internal result pointer to the next available result
  3606. *
  3607. * @return true on success, false if there is no more result set or an error object on failure
  3608. *
  3609. * @access public
  3610. */
  3611. function nextResult()
  3612. {
  3613. return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  3614. }
  3615. // }}}
  3616. // {{{ function getColumnNames()
  3617. /**
  3618. * Retrieve the names of columns returned by the DBMS in a query result or
  3619. * from the cache.
  3620. *
  3621. * @param bool If set to true the values are the column names,
  3622. * otherwise the names of the columns are the keys.
  3623. * @return mixed Array variable that holds the names of columns or an
  3624. * MDB2 error on failure.
  3625. * Some DBMS may not return any columns when the result set
  3626. * does not contain any rows.
  3627. *
  3628. * @access public
  3629. */
  3630. function getColumnNames($flip = false)
  3631. {
  3632. if (! isset($this->column_names))
  3633. {
  3634. $result = $this->_getColumnNames();
  3635. if (PEAR :: isError($result))
  3636. {
  3637. return $result;
  3638. }
  3639. $this->column_names = $result;
  3640. }
  3641. if ($flip)
  3642. {
  3643. return array_flip($this->column_names);
  3644. }
  3645. return $this->column_names;
  3646. }
  3647. // }}}
  3648. // {{{ function _getColumnNames()
  3649. /**
  3650. * Retrieve the names of columns returned by the DBMS in a query result.
  3651. *
  3652. * @return mixed Array variable that holds the names of columns as keys
  3653. * or an MDB2 error on failure.
  3654. * Some DBMS may not return any columns when the result set
  3655. * does not contain any rows.
  3656. *
  3657. * @access private
  3658. */
  3659. function _getColumnNames()
  3660. {
  3661. return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  3662. }
  3663. // }}}
  3664. // {{{ function numCols()
  3665. /**
  3666. * Count the number of columns returned by the DBMS in a query result.
  3667. *
  3668. * @return mixed integer value with the number of columns, a MDB2 error
  3669. * on failure
  3670. *
  3671. * @access public
  3672. */
  3673. function numCols()
  3674. {
  3675. return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__);
  3676. }
  3677. // }}}
  3678. // {{{ function getResource()
  3679. /**
  3680. * return the resource associated with the result object
  3681. *
  3682. * @return resource
  3683. *
  3684. * @access public
  3685. */
  3686. function getResource()
  3687. {
  3688. return $this->result;
  3689. }
  3690. // }}}
  3691. // {{{ function bindColumn($column, &$value, $type = null)
  3692. /**
  3693. * Set bind variable to a column.
  3694. *
  3695. * @param int column number or name
  3696. * @param mixed variable reference
  3697. * @param string specifies the type of the field
  3698. *
  3699. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3700. *
  3701. * @access public
  3702. */
  3703. function bindColumn($column, &$value, $type = null)
  3704. {
  3705. if (! is_numeric($column))
  3706. {
  3707. $column_names = $this->getColumnNames();
  3708. if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  3709. {
  3710. if ($this->db->options['field_case'] == CASE_LOWER)
  3711. {
  3712. $column = strtolower($column);
  3713. }
  3714. else
  3715. {
  3716. $column = strtoupper($column);
  3717. }
  3718. }
  3719. $column = $column_names[$column];
  3720. }
  3721. $this->values[$column] = & $value;
  3722. if (! is_null($type))
  3723. {
  3724. $this->types[$column] = $type;
  3725. }
  3726. return MDB2_OK;
  3727. }
  3728. // }}}
  3729. // {{{ function _assignBindColumns($row)
  3730. /**
  3731. * Bind a variable to a value in the result row.
  3732. *
  3733. * @param array row data
  3734. *
  3735. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3736. *
  3737. * @access private
  3738. */
  3739. function _assignBindColumns($row)
  3740. {
  3741. $row = array_values($row);
  3742. foreach ($row as $column => $value)
  3743. {
  3744. if (array_key_exists($column, $this->values))
  3745. {
  3746. $this->values[$column] = $value;
  3747. }
  3748. }
  3749. return MDB2_OK;
  3750. }
  3751. // }}}
  3752. // {{{ function free()
  3753. /**
  3754. * Free the internal resources associated with result.
  3755. *
  3756. * @return bool true on success, false if result is invalid
  3757. *
  3758. * @access public
  3759. */
  3760. function free()
  3761. {
  3762. $this->result = false;
  3763. return MDB2_OK;
  3764. }
  3765. // }}}
  3766. }
  3767. // }}}
  3768. // {{{ class MDB2_Row
  3769. /**
  3770. * The simple class that accepts row data as an array
  3771. *
  3772. * @package MDB2
  3773. * @category Database
  3774. * @author Lukas Smith <smith@pooteeweet.org>
  3775. */
  3776. class MDB2_Row
  3777. {
  3778. // {{{ constructor: function __construct(&$row)
  3779. /**
  3780. * constructor
  3781. *
  3782. * @param resource row data as array
  3783. */
  3784. function __construct(&$row)
  3785. {
  3786. foreach ($row as $key => $value)
  3787. {
  3788. $this->$key = &$row[$key];
  3789. }
  3790. }
  3791. // }}}
  3792. // {{{ function __construct(&$row)
  3793. /**
  3794. * PHP 4 Constructor
  3795. *
  3796. * @param resource row data as array
  3797. */
  3798. // function __construct(&$row)
  3799. // {
  3800. // $this->__construct($row);
  3801. // }
  3802. // }}}
  3803. }
  3804. // }}}
  3805. // {{{ class MDB2_Statement_Common
  3806. /**
  3807. * The common statement class for MDB2 statement objects
  3808. *
  3809. * @package MDB2
  3810. * @category Database
  3811. * @author Lukas Smith <smith@pooteeweet.org>
  3812. */
  3813. class MDB2_Statement_Common
  3814. {
  3815. // {{{ Variables (Properties)
  3816. var $db;
  3817. var $statement;
  3818. var $query;
  3819. var $result_types;
  3820. var $types;
  3821. var $values = array();
  3822. var $limit;
  3823. var $offset;
  3824. var $is_manip;
  3825. // }}}
  3826. // {{{ constructor: function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3827. /**
  3828. * Constructor
  3829. */
  3830. function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3831. {
  3832. $this->db = & $db;
  3833. $this->statement = & $statement;
  3834. $this->positions = $positions;
  3835. $this->query = $query;
  3836. $this->types = (array) $types;
  3837. $this->result_types = (array) $result_types;
  3838. $this->limit = $limit;
  3839. $this->is_manip = $is_manip;
  3840. $this->offset = $offset;
  3841. }
  3842. // }}}
  3843. // {{{ function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3844. /**
  3845. * PHP 4 Constructor
  3846. */
  3847. // function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3848. // {
  3849. // $this->__construct($db, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  3850. // }
  3851. // }}}
  3852. // {{{ function bindValue($parameter, &$value, $type = null)
  3853. /**
  3854. * Set the value of a parameter of a prepared query.
  3855. *
  3856. * @param int the order number of the parameter in the query
  3857. * statement. The order number of the first parameter is 1.
  3858. * @param mixed value that is meant to be assigned to specified
  3859. * parameter. The type of the value depends on the $type argument.
  3860. * @param string specifies the type of the field
  3861. *
  3862. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3863. *
  3864. * @access public
  3865. */
  3866. function bindValue($parameter, $value, $type = null)
  3867. {
  3868. if (! is_numeric($parameter))
  3869. {
  3870. $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
  3871. }
  3872. if (! in_array($parameter, $this->positions))
  3873. {
  3874. return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: ' . $parameter, __FUNCTION__);
  3875. }
  3876. $this->values[$parameter] = $value;
  3877. if (! is_null($type))
  3878. {
  3879. $this->types[$parameter] = $type;
  3880. }
  3881. return MDB2_OK;
  3882. }
  3883. // }}}
  3884. // {{{ function bindValueArray($values, $types = null)
  3885. /**
  3886. * Set the values of multiple a parameter of a prepared query in bulk.
  3887. *
  3888. * @param array specifies all necessary information
  3889. * for bindValue() the array elements must use keys corresponding to
  3890. * the number of the position of the parameter.
  3891. * @param array specifies the types of the fields
  3892. *
  3893. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3894. *
  3895. * @access public
  3896. * @see bindParam()
  3897. */
  3898. function bindValueArray($values, $types = null)
  3899. {
  3900. $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
  3901. $parameters = array_keys($values);
  3902. foreach ($parameters as $key => $parameter)
  3903. {
  3904. $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
  3905. $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  3906. $err = $this->bindValue($parameter, $values[$parameter], $types[$key]);
  3907. $this->db->popExpect();
  3908. $this->db->popErrorHandling();
  3909. if (PEAR :: isError($err))
  3910. {
  3911. if ($err->getCode() == MDB2_ERROR_NOT_FOUND)
  3912. {
  3913. //ignore (extra value for missing placeholder)
  3914. continue;
  3915. }
  3916. return $err;
  3917. }
  3918. }
  3919. return MDB2_OK;
  3920. }
  3921. // }}}
  3922. // {{{ function bindParam($parameter, &$value, $type = null)
  3923. /**
  3924. * Bind a variable to a parameter of a prepared query.
  3925. *
  3926. * @param int the order number of the parameter in the query
  3927. * statement. The order number of the first parameter is 1.
  3928. * @param mixed variable that is meant to be bound to specified
  3929. * parameter. The type of the value depends on the $type argument.
  3930. * @param string specifies the type of the field
  3931. *
  3932. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3933. *
  3934. * @access public
  3935. */
  3936. function bindParam($parameter, &$value, $type = null)
  3937. {
  3938. if (! is_numeric($parameter))
  3939. {
  3940. $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
  3941. }
  3942. if (! in_array($parameter, $this->positions))
  3943. {
  3944. return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: ' . $parameter, __FUNCTION__);
  3945. }
  3946. $this->values[$parameter] = & $value;
  3947. if (! is_null($type))
  3948. {
  3949. $this->types[$parameter] = $type;
  3950. }
  3951. return MDB2_OK;
  3952. }
  3953. // }}}
  3954. // {{{ function bindParamArray(&$values, $types = null)
  3955. /**
  3956. * Bind the variables of multiple a parameter of a prepared query in bulk.
  3957. *
  3958. * @param array specifies all necessary information
  3959. * for bindParam() the array elements must use keys corresponding to
  3960. * the number of the position of the parameter.
  3961. * @param array specifies the types of the fields
  3962. *
  3963. * @return mixed MDB2_OK on success, a MDB2 error on failure
  3964. *
  3965. * @access public
  3966. * @see bindParam()
  3967. */
  3968. function bindParamArray(&$values, $types = null)
  3969. {
  3970. $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
  3971. $parameters = array_keys($values);
  3972. foreach ($parameters as $key => $parameter)
  3973. {
  3974. $err = $this->bindParam($parameter, $values[$parameter], $types[$key]);
  3975. if (PEAR :: isError($err))
  3976. {
  3977. return $err;
  3978. }
  3979. }
  3980. return MDB2_OK;
  3981. }
  3982. // }}}
  3983. // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false)
  3984. /**
  3985. * Execute a prepared query statement.
  3986. *
  3987. * @param array specifies all necessary information
  3988. * for bindParam() the array elements must use keys corresponding
  3989. * to the number of the position of the parameter.
  3990. * @param mixed specifies which result class to use
  3991. * @param mixed specifies which class to wrap results in
  3992. *
  3993. * @return mixed MDB2_Result or integer (affected rows) on success,
  3994. * a MDB2 error on failure
  3995. * @access public
  3996. */
  3997. function &execute($values = null, $result_class = true, $result_wrap_class = false)
  3998. {
  3999. if (is_null($this->positions))
  4000. {
  4001. return $this->db->raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__);
  4002. }
  4003. $values = (array) $values;
  4004. if (! empty($values))
  4005. {
  4006. $err = $this->bindValueArray($values);
  4007. if (PEAR :: isError($err))
  4008. {
  4009. return $this->db->raiseError(MDB2_ERROR, null, null, 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__);
  4010. }
  4011. }
  4012. $result = & $this->_execute($result_class, $result_wrap_class);
  4013. return $result;
  4014. }
  4015. // }}}
  4016. // {{{ function &_execute($result_class = true, $result_wrap_class = false)
  4017. /**
  4018. * Execute a prepared query statement helper method.
  4019. *
  4020. * @param mixed specifies which result class to use
  4021. * @param mixed specifies which class to wrap results in
  4022. *
  4023. * @return mixed MDB2_Result or integer (affected rows) on success,
  4024. * a MDB2 error on failure
  4025. * @access private
  4026. */
  4027. function &_execute($result_class = true, $result_wrap_class = false)
  4028. {
  4029. $this->last_query = $this->query;
  4030. $query = '';
  4031. $last_position = 0;
  4032. foreach ($this->positions as $current_position => $parameter)
  4033. {
  4034. if (! array_key_exists($parameter, $this->values))
  4035. {
  4036. return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: ' . $parameter, __FUNCTION__);
  4037. }
  4038. $value = $this->values[$parameter];
  4039. $query .= substr($this->query, $last_position, $current_position - $last_position);
  4040. if (! isset($value))
  4041. {
  4042. $value_quoted = 'NULL';
  4043. }
  4044. else
  4045. {
  4046. $type = ! empty($this->types[$parameter]) ? $this->types[$parameter] : null;
  4047. $value_quoted = $this->db->quote($value, $type);
  4048. if (PEAR :: isError($value_quoted))
  4049. {
  4050. return $value_quoted;
  4051. }
  4052. }
  4053. $query .= $value_quoted;
  4054. $last_position = $current_position + 1;
  4055. }
  4056. $query .= substr($this->query, $last_position);
  4057. $this->db->offset = $this->offset;
  4058. $this->db->limit = $this->limit;
  4059. if ($this->is_manip)
  4060. {
  4061. $result = $this->db->exec($query);
  4062. }
  4063. else
  4064. {
  4065. $result = & $this->db->query($query, $this->result_types, $result_class, $result_wrap_class);
  4066. }
  4067. return $result;
  4068. }
  4069. // }}}
  4070. // {{{ function free()
  4071. /**
  4072. * Release resources allocated for the specified prepared query.
  4073. *
  4074. * @return mixed MDB2_OK on success, a MDB2 error on failure
  4075. *
  4076. * @access public
  4077. */
  4078. function free()
  4079. {
  4080. if (is_null($this->positions))
  4081. {
  4082. return $this->db->raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__);
  4083. }
  4084. $this->statement = null;
  4085. $this->positions = null;
  4086. $this->query = null;
  4087. $this->types = null;
  4088. $this->result_types = null;
  4089. $this->limit = null;
  4090. $this->is_manip = null;
  4091. $this->offset = null;
  4092. $this->values = null;
  4093. return MDB2_OK;
  4094. }
  4095. // }}}
  4096. }
  4097. // }}}
  4098. // {{{ class MDB2_Module_Common
  4099. /**
  4100. * The common modules class for MDB2 module objects
  4101. *
  4102. * @package MDB2
  4103. * @category Database
  4104. * @author Lukas Smith <smith@pooteeweet.org>
  4105. */
  4106. class MDB2_Module_Common
  4107. {
  4108. // {{{ Variables (Properties)
  4109. /**
  4110. * contains the key to the global MDB2 instance array of the associated
  4111. * MDB2 instance
  4112. *
  4113. * @var int
  4114. * @access protected
  4115. */
  4116. var $db_index;
  4117. // }}}
  4118. // {{{ constructor: function __construct($db_index)
  4119. /**
  4120. * Constructor
  4121. */
  4122. function __construct($db_index)
  4123. {
  4124. $this->db_index = $db_index;
  4125. }
  4126. // }}}
  4127. // {{{ function __construct($db_index)
  4128. /**
  4129. * PHP 4 Constructor
  4130. */
  4131. // function __construct($db_index)
  4132. // {
  4133. // $this->__construct($db_index);
  4134. // }
  4135. // }}}
  4136. // {{{ function &getDBInstance()
  4137. /**
  4138. * Get the instance of MDB2 associated with the module instance
  4139. *
  4140. * @return object MDB2 instance or a MDB2 error on failure
  4141. *
  4142. * @access public
  4143. */
  4144. function &getDBInstance()
  4145. {
  4146. if (isset($GLOBALS['_MDB2_databases'][$this->db_index]))
  4147. {
  4148. $result = & $GLOBALS['_MDB2_databases'][$this->db_index];
  4149. }
  4150. else
  4151. {
  4152. $result = & MDB2 :: raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'could not find MDB2 instance');
  4153. }
  4154. return $result;
  4155. }
  4156. // }}}
  4157. }
  4158. // }}}
  4159. // {{{ function MDB2_closeOpenTransactions()
  4160. /**
  4161. * Close any open transactions form persistent connections
  4162. *
  4163. * @return void
  4164. *
  4165. * @access public
  4166. */
  4167. function MDB2_closeOpenTransactions()
  4168. {
  4169. reset($GLOBALS['_MDB2_databases']);
  4170. while (next($GLOBALS['_MDB2_databases']))
  4171. {
  4172. $key = key($GLOBALS['_MDB2_databases']);
  4173. if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent && $GLOBALS['_MDB2_databases'][$key]->in_transaction)
  4174. {
  4175. $GLOBALS['_MDB2_databases'][$key]->rollback();
  4176. }
  4177. }
  4178. }
  4179. // }}}
  4180. // {{{ function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null)
  4181. /**
  4182. * default debug output handler
  4183. *
  4184. * @param object reference to an MDB2 database object
  4185. * @param string usually the method name that triggered the debug call:
  4186. * for example 'query', 'prepare', 'execute', 'parameters',
  4187. * 'beginTransaction', 'commit', 'rollback'
  4188. * @param string message that should be appended to the debug variable
  4189. * @param array contains context information about the debug() call
  4190. * common keys are: is_manip, time, result etc.
  4191. *
  4192. * @return void|string optionally return a modified message, this allows
  4193. * rewriting a query before being issued or prepared
  4194. *
  4195. * @access public
  4196. */
  4197. function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array())
  4198. {
  4199. $db->debug_output .= $scope . '(' . $db->db_index . '): ';
  4200. $db->debug_output .= $message . $db->getOption('log_line_break');
  4201. return $message;
  4202. }
  4203. // }}}
  4204. ?>