sqlite3.php 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Robin Appelman
  6. * @copyright 2011 Robin Appelman icewind1991@gmail.com
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * MDB2 SQLite3 driver
  24. *
  25. * @package MDB2
  26. * @category Database
  27. * @author Lukas Smith <smith@pooteeweet.org>
  28. */
  29. class MDB2_Driver_sqlite3 extends MDB2_Driver_Common
  30. {
  31. // {{{ properties
  32. public $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false);
  33. public $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
  34. public $_lasterror = '';
  35. public $fix_assoc_fields_names = false;
  36. // }}}
  37. // {{{ constructor
  38. /**
  39. * Constructor
  40. */
  41. function __construct()
  42. {
  43. parent::__construct();
  44. $this->phptype = 'sqlite3';
  45. $this->dbsyntax = 'sqlite';
  46. $this->supported['sequences'] = 'emulated';
  47. $this->supported['indexes'] = true;
  48. $this->supported['affected_rows'] = true;
  49. $this->supported['summary_functions'] = true;
  50. $this->supported['order_by_text'] = true;
  51. $this->supported['current_id'] = 'emulated';
  52. $this->supported['limit_queries'] = true;
  53. $this->supported['LOBs'] = true;
  54. $this->supported['replace'] = true;
  55. $this->supported['transactions'] = false;
  56. $this->supported['savepoints'] = false;
  57. $this->supported['sub_selects'] = true;
  58. $this->supported['triggers'] = true;
  59. $this->supported['auto_increment'] = true;
  60. $this->supported['primary_key'] = false; // requires alter table implementation
  61. $this->supported['result_introspection'] = false; // not implemented
  62. $this->supported['prepared_statements'] = true;
  63. $this->supported['identifier_quoting'] = true;
  64. $this->supported['pattern_escaping'] = false;
  65. $this->supported['new_link'] = false;
  66. $this->options['DBA_username'] = false;
  67. $this->options['DBA_password'] = false;
  68. $this->options['base_transaction_name'] = '___php_MDB2_sqlite_auto_commit_off';
  69. $this->options['fixed_float'] = 0;
  70. $this->options['database_path'] = '';
  71. $this->options['database_extension'] = '';
  72. $this->options['server_version'] = '';
  73. $this->options['max_identifiers_length'] = 128; //no real limit
  74. }
  75. // }}}
  76. // {{{ errorInfo()
  77. /**
  78. * This method is used to collect information about an error
  79. *
  80. * @param integer $error
  81. * @return array
  82. * @access public
  83. */
  84. function errorInfo($error = null)
  85. {
  86. $native_code = null;
  87. if ($this->connection) {
  88. $native_code = $this->connection->lastErrorCode();
  89. }
  90. $native_msg = $this->_lasterror
  91. ? html_entity_decode($this->_lasterror) : $this->connection->lastErrorMsg();
  92. // PHP 5.2+ prepends the function name to $php_errormsg, so we need
  93. // this hack to work around it, per bug #9599.
  94. $native_msg = preg_replace('/^sqlite[a-z_]+\(\)[^:]*: /', '', $native_msg);
  95. if (is_null($error)) {
  96. static $error_regexps;
  97. if (empty($error_regexps)) {
  98. $error_regexps = array(
  99. '/^no such table:/' => MDB2_ERROR_NOSUCHTABLE,
  100. '/^no such index:/' => MDB2_ERROR_NOT_FOUND,
  101. '/^(table|index) .* already exists$/' => MDB2_ERROR_ALREADY_EXISTS,
  102. '/PRIMARY KEY must be unique/i' => MDB2_ERROR_CONSTRAINT,
  103. '/is not unique/' => MDB2_ERROR_CONSTRAINT,
  104. '/columns .* are not unique/i' => MDB2_ERROR_CONSTRAINT,
  105. '/uniqueness constraint failed/' => MDB2_ERROR_CONSTRAINT,
  106. '/may not be NULL/' => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  107. '/^no such column:/' => MDB2_ERROR_NOSUCHFIELD,
  108. '/no column named/' => MDB2_ERROR_NOSUCHFIELD,
  109. '/column not present in both tables/i' => MDB2_ERROR_NOSUCHFIELD,
  110. '/^near ".*": syntax error$/' => MDB2_ERROR_SYNTAX,
  111. '/[0-9]+ values for [0-9]+ columns/i' => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  112. );
  113. }
  114. foreach ($error_regexps as $regexp => $code) {
  115. if (preg_match($regexp, $native_msg)) {
  116. $error = $code;
  117. break;
  118. }
  119. }
  120. }
  121. return array($error, $native_code, $native_msg);
  122. }
  123. // }}}
  124. // {{{ escape()
  125. /**
  126. * Quotes a string so it can be safely used in a query. It will quote
  127. * the text so it can safely be used within a query.
  128. *
  129. * @param string the input string to quote
  130. * @param bool escape wildcards
  131. *
  132. * @return string quoted string
  133. *
  134. * @access public
  135. */
  136. public function escape($text, $escape_wildcards = false)
  137. {
  138. if($this->connection) {
  139. return $this->connection->escapeString($text);
  140. }else{
  141. return str_replace("'", "''", $text);//TODO; more
  142. }
  143. }
  144. // }}}
  145. // {{{ beginTransaction()
  146. /**
  147. * Start a transaction or set a savepoint.
  148. *
  149. * @param string name of a savepoint to set
  150. * @return mixed MDB2_OK on success, a MDB2 error on failure
  151. *
  152. * @access public
  153. */
  154. function beginTransaction($savepoint = null)
  155. {
  156. $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  157. if (!is_null($savepoint)) {
  158. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  159. 'savepoints are not supported', __FUNCTION__);
  160. } elseif ($this->in_transaction) {
  161. return MDB2_OK; //nothing to do
  162. }
  163. if (!$this->destructor_registered && $this->opened_persistent) {
  164. $this->destructor_registered = true;
  165. register_shutdown_function('MDB2_closeOpenTransactions');
  166. }
  167. $query = 'BEGIN TRANSACTION '.$this->options['base_transaction_name'];
  168. $result =$this->_doQuery($query, true);
  169. if (PEAR::isError($result)) {
  170. return $result;
  171. }
  172. $this->in_transaction = true;
  173. return MDB2_OK;
  174. }
  175. // }}}
  176. // {{{ commit()
  177. /**
  178. * Commit the database changes done during a transaction that is in
  179. * progress or release a savepoint. This function may only be called when
  180. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  181. * transaction is implicitly started after committing the pending changes.
  182. *
  183. * @param string name of a savepoint to release
  184. * @return mixed MDB2_OK on success, a MDB2 error on failure
  185. *
  186. * @access public
  187. */
  188. function commit($savepoint = null)
  189. {
  190. $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  191. if (!$this->in_transaction) {
  192. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  193. 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
  194. }
  195. if (!is_null($savepoint)) {
  196. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  197. 'savepoints are not supported', __FUNCTION__);
  198. }
  199. $query = 'COMMIT TRANSACTION '.$this->options['base_transaction_name'];
  200. $result =$this->_doQuery($query, true);
  201. if (PEAR::isError($result)) {
  202. return $result;
  203. }
  204. $this->in_transaction = false;
  205. return MDB2_OK;
  206. }
  207. // }}}
  208. // {{{
  209. /**
  210. * Cancel any database changes done during a transaction or since a specific
  211. * savepoint that is in progress. This function may only be called when
  212. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  213. * transaction is implicitly started after canceling the pending changes.
  214. *
  215. * @param string name of a savepoint to rollback to
  216. * @return mixed MDB2_OK on success, a MDB2 error on failure
  217. *
  218. * @access public
  219. */
  220. function rollback($savepoint = null)
  221. {
  222. $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  223. if (!$this->in_transaction) {
  224. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  225. 'rollback cannot be done changes are auto committed', __FUNCTION__);
  226. }
  227. if (!is_null($savepoint)) {
  228. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  229. 'savepoints are not supported', __FUNCTION__);
  230. }
  231. $query = 'ROLLBACK TRANSACTION '.$this->options['base_transaction_name'];
  232. $result =$this->_doQuery($query, true);
  233. if (PEAR::isError($result)) {
  234. return $result;
  235. }
  236. $this->in_transaction = false;
  237. return MDB2_OK;
  238. }
  239. // }}}
  240. // {{{ function setTransactionIsolation()
  241. /**
  242. * Set the transacton isolation level.
  243. *
  244. * @param string standard isolation level
  245. * READ UNCOMMITTED (allows dirty reads)
  246. * READ COMMITTED (prevents dirty reads)
  247. * REPEATABLE READ (prevents nonrepeatable reads)
  248. * SERIALIZABLE (prevents phantom reads)
  249. * @return mixed MDB2_OK on success, a MDB2 error on failure
  250. *
  251. * @access public
  252. * @since 2.1.1
  253. */
  254. function setTransactionIsolation($isolation, $options=array())
  255. {
  256. $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
  257. switch ($isolation) {
  258. case 'READ UNCOMMITTED':
  259. $isolation = 0;
  260. break;
  261. case 'READ COMMITTED':
  262. case 'REPEATABLE READ':
  263. case 'SERIALIZABLE':
  264. $isolation = 1;
  265. break;
  266. default:
  267. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  268. 'isolation level is not supported: '.$isolation, __FUNCTION__);
  269. }
  270. $query = "PRAGMA read_uncommitted=$isolation";
  271. return $this->_doQuery($query, true);
  272. }
  273. // }}}
  274. // {{{ getDatabaseFile()
  275. /**
  276. * Builds the string with path+dbname+extension
  277. *
  278. * @return string full database path+file
  279. * @access protected
  280. */
  281. function _getDatabaseFile($database_name)
  282. {
  283. if ($database_name === '' || $database_name === ':memory:') {
  284. return $database_name;
  285. }
  286. return $this->options['database_path'].$database_name.$this->options['database_extension'];
  287. }
  288. // }}}
  289. // {{{ connect()
  290. /**
  291. * Connect to the database
  292. *
  293. * @return true on success, MDB2 Error Object on failure
  294. **/
  295. function connect()
  296. {
  297. if($this->connection instanceof SQLite3) {
  298. return MDB2_OK;
  299. }
  300. $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" );
  301. $database_file = $this->_getDatabaseFile($this->database_name);
  302. if (is_resource($this->connection)) {
  303. //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
  304. if (MDB2::areEquals($this->connected_dsn, $this->dsn)
  305. && $this->connected_database_name == $database_file
  306. && $this->opened_persistent == $this->options['persistent']
  307. ) {
  308. return MDB2_OK;
  309. }
  310. $this->disconnect(false);
  311. }
  312. if (!PEAR::loadExtension($this->phptype)) {
  313. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  314. 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
  315. }
  316. if (empty($this->database_name)) {
  317. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
  318. 'unable to establish a connection', __FUNCTION__);
  319. }
  320. if ($database_file !== ':memory:') {
  321. if(!strpos($database_file, '.db')) {
  322. $database_file="$datadir/$database_file.db";
  323. }
  324. if (!file_exists($database_file)) {
  325. if (!touch($database_file)) {
  326. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  327. 'Could not create database file', __FUNCTION__);
  328. }
  329. if (!isset($this->dsn['mode'])
  330. || !is_numeric($this->dsn['mode'])
  331. ) {
  332. $mode = 0644;
  333. } else {
  334. $mode = octdec($this->dsn['mode']);
  335. }
  336. if (!chmod($database_file, $mode)) {
  337. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  338. 'Could not be chmodded database file', __FUNCTION__);
  339. }
  340. if (!file_exists($database_file)) {
  341. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  342. 'Could not be found database file', __FUNCTION__);
  343. }
  344. }
  345. if (!is_file($database_file)) {
  346. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  347. 'Database is a directory name', __FUNCTION__);
  348. }
  349. if (!is_readable($database_file)) {
  350. return $this->raiseError(MDB2_ERROR_ACCESS_VIOLATION, null, null,
  351. 'Could not read database file', __FUNCTION__);
  352. }
  353. }
  354. $php_errormsg = '';
  355. $this->connection = new SQLite3($database_file);
  356. if(is_callable(array($this->connection, 'busyTimeout'))) {//busy timout is only available in php>=5.3
  357. $this->connection->busyTimeout(100);
  358. }
  359. $this->_lasterror = $this->connection->lastErrorMsg();
  360. if (!$this->connection) {
  361. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
  362. 'unable to establish a connection', __FUNCTION__);
  363. }
  364. if ($this->fix_assoc_fields_names ||
  365. $this->options['portability'] & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) {
  366. $this->connection->exec("PRAGMA short_column_names = 1");
  367. $this->fix_assoc_fields_names = true;
  368. }
  369. $this->connected_dsn = $this->dsn;
  370. $this->connected_database_name = $database_file;
  371. $this->opened_persistent = $this->getoption('persistent');
  372. $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
  373. return MDB2_OK;
  374. }
  375. // }}}
  376. // {{{ databaseExists()
  377. /**
  378. * check if given database name is exists?
  379. *
  380. * @param string $name name of the database that should be checked
  381. *
  382. * @return mixed true/false on success, a MDB2 error on failure
  383. * @access public
  384. */
  385. function databaseExists($name)
  386. {
  387. $database_file = $this->_getDatabaseFile($name);
  388. $result = file_exists($database_file);
  389. return $result;
  390. }
  391. // }}}
  392. // {{{ disconnect()
  393. /**
  394. * Log out and disconnect from the database.
  395. *
  396. * @param boolean $force if the disconnect should be forced even if the
  397. * connection is opened persistently
  398. * @return mixed true on success, false if not connected and error
  399. * object on error
  400. * @access public
  401. */
  402. function disconnect($force = true)
  403. {
  404. if ($this->connection instanceof SQLite3) {
  405. if ($this->in_transaction) {
  406. $dsn = $this->dsn;
  407. $database_name = $this->database_name;
  408. $persistent = $this->options['persistent'];
  409. $this->dsn = $this->connected_dsn;
  410. $this->database_name = $this->connected_database_name;
  411. $this->options['persistent'] = $this->opened_persistent;
  412. $this->rollback();
  413. $this->dsn = $dsn;
  414. $this->database_name = $database_name;
  415. $this->options['persistent'] = $persistent;
  416. }
  417. if (!$this->opened_persistent || $force) {
  418. $this->connection->close();
  419. }
  420. } else {
  421. return false;
  422. }
  423. return parent::disconnect($force);
  424. }
  425. // }}}
  426. // {{{ _doQuery()
  427. /**
  428. * Execute a query
  429. * @param string $query query
  430. * @param boolean $is_manip if the query is a manipulation query
  431. * @param resource $connection
  432. * @param string $database_name
  433. * @return result or error object
  434. * @access protected
  435. */
  436. function _doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  437. {
  438. $this->last_query = $query;
  439. $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  440. if ($result) {
  441. if (PEAR::isError($result)) {
  442. return $result;
  443. }
  444. $query = $result;
  445. }
  446. if ($this->options['disable_query']) {
  447. $result = $is_manip ? 0 : null;
  448. return $result;
  449. }
  450. $result=$this->connection->query($query.';');
  451. $this->_lasterror = $this->connection->lastErrorMsg();
  452. if (!$result) {
  453. $err =$this->raiseError(null, null, null,
  454. 'Could not execute statement', __FUNCTION__);
  455. return $err;
  456. }
  457. $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
  458. return $result;
  459. }
  460. // }}}
  461. // {{{ _affectedRows()
  462. /**
  463. * Returns the number of rows affected
  464. *
  465. * @param resource $result
  466. * @param resource $connection
  467. * @return mixed MDB2 Error Object or the number of rows affected
  468. * @access private
  469. */
  470. function _affectedRows($connection, $result = null)
  471. {
  472. return $this->connection->changes();
  473. }
  474. // }}}
  475. // {{{ _modifyQuery()
  476. /**
  477. * Changes a query string for various DBMS specific reasons
  478. *
  479. * @param string $query query to modify
  480. * @param boolean $is_manip if it is a DML query
  481. * @param integer $limit limit the number of rows
  482. * @param integer $offset start reading from given offset
  483. * @return string modified query
  484. * @access protected
  485. */
  486. function _modifyQuery($query, $is_manip, $limit, $offset)
  487. {
  488. if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) {
  489. if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
  490. $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
  491. 'DELETE FROM \1 WHERE 1=1', $query);
  492. }
  493. }
  494. if ($limit > 0
  495. && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query)
  496. ) {
  497. $query = rtrim($query);
  498. if (substr($query, -1) == ';') {
  499. $query = substr($query, 0, -1);
  500. }
  501. if ($is_manip) {
  502. $query.= " LIMIT $limit";
  503. } else {
  504. $query.= " LIMIT $offset,$limit";
  505. }
  506. }
  507. return $query;
  508. }
  509. // }}}
  510. // {{{ getServerVersion()
  511. /**
  512. * return version information about the server
  513. *
  514. * @param bool $native determines if the raw version string should be returned
  515. * @return mixed array/string with version information or MDB2 error object
  516. * @access public
  517. */
  518. function getServerVersion($native = false)
  519. {
  520. $server_info = false;
  521. if ($this->connected_server_info) {
  522. $server_info = $this->connected_server_info;
  523. } elseif ($this->options['server_version']) {
  524. $server_info = $this->options['server_version'];
  525. } elseif (function_exists('sqlite_libversion')) {
  526. $server_info = @sqlite_libversion();
  527. }
  528. if (!$server_info) {
  529. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  530. 'Requires either the "server_version" option or the sqlite_libversion() function', __FUNCTION__);
  531. }
  532. // cache server_info
  533. $this->connected_server_info = $server_info;
  534. if (!$native) {
  535. $tmp = explode('.', $server_info, 3);
  536. $server_info = array(
  537. 'major' => isset($tmp[0]) ? $tmp[0] : null,
  538. 'minor' => isset($tmp[1]) ? $tmp[1] : null,
  539. 'patch' => isset($tmp[2]) ? $tmp[2] : null,
  540. 'extra' => null,
  541. 'native' => $server_info,
  542. );
  543. }
  544. return $server_info;
  545. }
  546. // }}}
  547. // {{{ replace()
  548. /**
  549. * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
  550. * query, except that if there is already a row in the table with the same
  551. * key field values, the old row is deleted before the new row is inserted.
  552. *
  553. * The REPLACE type of query does not make part of the SQL standards. Since
  554. * practically only SQLite implements it natively, this type of query is
  555. * emulated through this method for other DBMS using standard types of
  556. * queries inside a transaction to assure the atomicity of the operation.
  557. *
  558. * @access public
  559. *
  560. * @param string $table name of the table on which the REPLACE query will
  561. * be executed.
  562. * @param array $fields associative array that describes the fields and the
  563. * values that will be inserted or updated in the specified table. The
  564. * indexes of the array are the names of all the fields of the table. The
  565. * values of the array are also associative arrays that describe the
  566. * values and other properties of the table fields.
  567. *
  568. * Here follows a list of field properties that need to be specified:
  569. *
  570. * value:
  571. * Value to be assigned to the specified field. This value may be
  572. * of specified in database independent type format as this
  573. * function can perform the necessary datatype conversions.
  574. *
  575. * Default:
  576. * this property is required unless the Null property
  577. * is set to 1.
  578. *
  579. * type
  580. * Name of the type of the field. Currently, all types Metabase
  581. * are supported except for clob and blob.
  582. *
  583. * Default: no type conversion
  584. *
  585. * null
  586. * Boolean property that indicates that the value for this field
  587. * should be set to null.
  588. *
  589. * The default value for fields missing in INSERT queries may be
  590. * specified the definition of a table. Often, the default value
  591. * is already null, but since the REPLACE may be emulated using
  592. * an UPDATE query, make sure that all fields of the table are
  593. * listed in this function argument array.
  594. *
  595. * Default: 0
  596. *
  597. * key
  598. * Boolean property that indicates that this field should be
  599. * handled as a primary key or at least as part of the compound
  600. * unique index of the table that will determine the row that will
  601. * updated if it exists or inserted a new row otherwise.
  602. *
  603. * This function will fail if no key field is specified or if the
  604. * value of a key field is set to null because fields that are
  605. * part of unique index they may not be null.
  606. *
  607. * Default: 0
  608. *
  609. * @return mixed MDB2_OK on success, a MDB2 error on failure
  610. */
  611. function replace($table, $fields)
  612. {
  613. $count = count($fields);
  614. $query = $values = '';
  615. $keys = $colnum = 0;
  616. for (reset($fields); $colnum < $count; next($fields), $colnum++) {
  617. $name = key($fields);
  618. if ($colnum > 0) {
  619. $query .= ',';
  620. $values.= ',';
  621. }
  622. $query.= $this->quoteIdentifier($name, true);
  623. if (isset($fields[$name]['null']) && $fields[$name]['null']) {
  624. $value = 'NULL';
  625. } else {
  626. $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
  627. $value = $this->quote($fields[$name]['value'], $type);
  628. if (PEAR::isError($value)) {
  629. return $value;
  630. }
  631. }
  632. $values.= $value;
  633. if (isset($fields[$name]['key']) && $fields[$name]['key']) {
  634. if ($value === 'NULL') {
  635. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  636. 'key value '.$name.' may not be NULL', __FUNCTION__);
  637. }
  638. $keys++;
  639. }
  640. }
  641. if ($keys == 0) {
  642. return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  643. 'not specified which fields are keys', __FUNCTION__);
  644. }
  645. $connection = $this->getConnection();
  646. if (PEAR::isError($connection)) {
  647. return $connection;
  648. }
  649. $table = $this->quoteIdentifier($table, true);
  650. $query = "REPLACE INTO $table ($query) VALUES ($values)";
  651. $result =$this->_doQuery($query, true, $connection);
  652. if (PEAR::isError($result)) {
  653. return $result;
  654. }
  655. return $this->_affectedRows($connection, $result);
  656. }
  657. // }}}
  658. // {{{ nextID()
  659. /**
  660. * Returns the next free id of a sequence
  661. *
  662. * @param string $seq_name name of the sequence
  663. * @param boolean $ondemand when true the sequence is
  664. * automatic created, if it
  665. * not exists
  666. *
  667. * @return mixed MDB2 Error Object or id
  668. * @access public
  669. */
  670. function nextID($seq_name, $ondemand = true)
  671. {
  672. $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
  673. $seqcol_name = $this->options['seqcol_name'];
  674. $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)";
  675. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  676. $this->expectError(MDB2_ERROR_NOSUCHTABLE);
  677. $result =$this->_doQuery($query, true);
  678. $this->popExpect();
  679. $this->popErrorHandling();
  680. if (PEAR::isError($result)) {
  681. if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
  682. $this->loadModule('Manager', null, true);
  683. $result = $this->manager->createSequence($seq_name);
  684. if (PEAR::isError($result)) {
  685. return $this->raiseError($result, null, null,
  686. 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
  687. } else {
  688. return $this->nextID($seq_name, false);
  689. }
  690. }
  691. return $result;
  692. }
  693. $value = $this->lastInsertID();
  694. if (is_numeric($value)) {
  695. $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
  696. $result =$this->_doQuery($query, true);
  697. if (PEAR::isError($result)) {
  698. $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
  699. }
  700. }
  701. return $value;
  702. }
  703. // }}}
  704. // {{{ lastInsertID()
  705. /**
  706. * Returns the autoincrement ID if supported or $id or fetches the current
  707. * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  708. *
  709. * @param string $table name of the table into which a new row was inserted
  710. * @param string $field name of the field into which a new row was inserted
  711. * @return mixed MDB2 Error Object or id
  712. * @access public
  713. */
  714. function lastInsertID($table = null, $field = null)
  715. {
  716. return $this->connection->lastInsertRowID();
  717. }
  718. // }}}
  719. // {{{ currID()
  720. /**
  721. * Returns the current id of a sequence
  722. *
  723. * @param string $seq_name name of the sequence
  724. * @return mixed MDB2 Error Object or id
  725. * @access public
  726. */
  727. function currID($seq_name)
  728. {
  729. $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
  730. $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
  731. $query = "SELECT MAX($seqcol_name) FROM $sequence_name";
  732. return $this->queryOne($query, 'integer');
  733. }
  734. /**
  735. * Prepares a query for multiple execution with execute().
  736. * With some database backends, this is emulated.
  737. * prepare() requires a generic query as string like
  738. * 'INSERT INTO numbers VALUES(?,?)' or
  739. * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  740. * The ? and :name and are placeholders which can be set using
  741. * bindParam() and the query can be sent off using the execute() method.
  742. * The allowed format for :name can be set with the 'bindname_format' option.
  743. *
  744. * @param string $query the query to prepare
  745. * @param mixed $types array that contains the types of the placeholders
  746. * @param mixed $result_types array that contains the types of the columns in
  747. * the result set or MDB2_PREPARE_RESULT, if set to
  748. * MDB2_PREPARE_MANIP the query is handled as a manipulation query
  749. * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders
  750. * @return mixed resource handle for the prepared query on success, a MDB2
  751. * error on failure
  752. * @access public
  753. * @see bindParam, execute
  754. */
  755. function prepare($query, $types = null, $result_types = null, $lobs = array())
  756. {
  757. if ($this->options['emulate_prepared']
  758. || $this->supported['prepared_statements'] !== true
  759. ) {
  760. $obj =& parent::prepare($query, $types, $result_types, $lobs);
  761. return $obj;
  762. }
  763. $this->last_query = $query;
  764. $is_manip = ($result_types === MDB2_PREPARE_MANIP);
  765. $offset = $this->offset;
  766. $limit = $this->limit;
  767. $this->offset = $this->limit = 0;
  768. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  769. $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
  770. if ($result) {
  771. if (PEAR::isError($result)) {
  772. return $result;
  773. }
  774. $query = $result;
  775. }
  776. $placeholder_type_guess = $placeholder_type = null;
  777. $question = '?';
  778. $colon = ':';
  779. $positions = array();
  780. $position = 0;
  781. while ($position < strlen($query)) {
  782. $q_position = strpos($query, $question, $position);
  783. $c_position = strpos($query, $colon, $position);
  784. if ($q_position && $c_position) {
  785. $p_position = min($q_position, $c_position);
  786. } elseif ($q_position) {
  787. $p_position = $q_position;
  788. } elseif ($c_position) {
  789. $p_position = $c_position;
  790. } else {
  791. break;
  792. }
  793. if (is_null($placeholder_type)) {
  794. $placeholder_type_guess = $query[$p_position];
  795. }
  796. $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
  797. if (PEAR::isError($new_pos)) {
  798. return $new_pos;
  799. }
  800. if ($new_pos != $position) {
  801. $position = $new_pos;
  802. continue; //evaluate again starting from the new position
  803. }
  804. if ($query[$position] == $placeholder_type_guess) {
  805. if (is_null($placeholder_type)) {
  806. $placeholder_type = $query[$p_position];
  807. $question = $colon = $placeholder_type;
  808. }
  809. if ($placeholder_type == ':') {
  810. $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s';
  811. $parameter = preg_replace($regexp, '\\1', $query);
  812. if ($parameter === '') {
  813. $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  814. 'named parameter name must match "bindname_format" option', __FUNCTION__);
  815. return $err;
  816. }
  817. $positions[$p_position] = $parameter;
  818. $query = substr_replace($query, '?', $position, strlen($parameter)+1);
  819. } else {
  820. $positions[$p_position] = count($positions);
  821. }
  822. $position = $p_position + 1;
  823. } else {
  824. $position = $p_position;
  825. }
  826. }
  827. $connection = $this->getConnection();
  828. if (PEAR::isError($connection)) {
  829. return $connection;
  830. }
  831. $statement =$this->connection->prepare($query);
  832. if (!$statement) {
  833. return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  834. 'unable to prepare statement: '.$query);
  835. }
  836. $class_name = 'MDB2_Statement_'.$this->phptype;
  837. $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  838. $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
  839. return $obj;
  840. }
  841. }
  842. /**
  843. * MDB2 SQLite result driver
  844. *
  845. * @package MDB2
  846. * @category Database
  847. * @author Lukas Smith <smith@pooteeweet.org>
  848. */
  849. class MDB2_Result_sqlite3 extends MDB2_Result_Common
  850. {
  851. // }}}
  852. // {{{ fetchRow()
  853. /**
  854. * Fetch a row and insert the data into an existing array.
  855. *
  856. * @param int $fetchmode how the array data should be indexed
  857. * @param int $rownum number of the row where the data can be found
  858. * @return int data array on success, a MDB2 error on failure
  859. * @access public
  860. */
  861. function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  862. {
  863. if (!is_null($rownum)) {
  864. $seek = $this->seek($rownum);
  865. if (PEAR::isError($seek)) {
  866. return $seek;
  867. }
  868. }
  869. if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
  870. $fetchmode = $this->db->fetchmode;
  871. }
  872. if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
  873. //$row = @sqlite_fetch_array($this->result, SQLITE_ASSOC);
  874. $row=$this->result->fetchArray(SQLITE3_ASSOC);
  875. if (is_array($row)
  876. && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
  877. ) {
  878. $row = array_change_key_case($row, $this->db->options['field_case']);
  879. }
  880. } else {
  881. $row=$this->result->fetchArray(SQLITE3_NUM);
  882. }
  883. if (!$row) {
  884. if ($this->result === false) {
  885. $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  886. 'resultset has already been freed', __FUNCTION__);
  887. return $err;
  888. }
  889. $null = null;
  890. return $null;
  891. }
  892. $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
  893. $rtrim = false;
  894. if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
  895. if (empty($this->types)) {
  896. $mode += MDB2_PORTABILITY_RTRIM;
  897. } else {
  898. $rtrim = true;
  899. }
  900. }
  901. if ($mode) {
  902. $this->db->_fixResultArrayValues($row, $mode);
  903. }
  904. if (!empty($this->types)) {
  905. $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
  906. }
  907. if (!empty($this->values)) {
  908. $this->_assignBindColumns($row);
  909. }
  910. if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
  911. $object_class = $this->db->options['fetch_class'];
  912. if ($object_class == 'stdClass') {
  913. $row = (object) $row;
  914. } else {
  915. $row = new $object_class($row);
  916. }
  917. }
  918. ++$this->rownum;
  919. return $row;
  920. }
  921. // }}}
  922. // {{{ _getColumnNames()
  923. /**
  924. * Retrieve the names of columns returned by the DBMS in a query result.
  925. *
  926. * @return mixed Array variable that holds the names of columns as keys
  927. * or an MDB2 error on failure.
  928. * Some DBMS may not return any columns when the result set
  929. * does not contain any rows.
  930. * @access private
  931. */
  932. function _getColumnNames()
  933. {
  934. $columns = array();
  935. $numcols = $this->numCols();
  936. if (PEAR::isError($numcols)) {
  937. return $numcols;
  938. }
  939. for ($column = 0; $column < $numcols; $column++) {
  940. $column_name = $this->result->getColumnName($column);
  941. $columns[$column_name] = $column;
  942. }
  943. if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  944. $columns = array_change_key_case($columns, $this->db->options['field_case']);
  945. }
  946. return $columns;
  947. }
  948. // }}}
  949. // {{{ numCols()
  950. /**
  951. * Count the number of columns returned by the DBMS in a query result.
  952. *
  953. * @access public
  954. * @return mixed integer value with the number of columns, a MDB2 error
  955. * on failure
  956. */
  957. function numCols()
  958. {
  959. $this->result->numColumns();
  960. }
  961. }
  962. /**
  963. * MDB2 SQLite buffered result driver
  964. *
  965. * @package MDB2
  966. * @category Database
  967. * @author Lukas Smith <smith@pooteeweet.org>
  968. */
  969. class MDB2_BufferedResult_sqlite3 extends MDB2_Result_sqlite3
  970. {
  971. // {{{ seek()
  972. /**
  973. * Seek to a specific row in a result set
  974. *
  975. * @param int $rownum number of the row where the data can be found
  976. * @return mixed MDB2_OK on success, a MDB2 error on failure
  977. * @access public
  978. */
  979. function seek($rownum = 0)
  980. {
  981. $this->result->reset();
  982. for($i=0;$i<$rownum;$i++) {
  983. $this->result->fetchArray();
  984. }
  985. $this->rownum = $rownum - 1;
  986. return MDB2_OK;
  987. }
  988. // }}}
  989. // {{{ valid()
  990. /**
  991. * Check if the end of the result set has been reached
  992. *
  993. * @return mixed true or false on sucess, a MDB2 error on failure
  994. * @access public
  995. */
  996. function valid()
  997. {
  998. $numrows = $this->numRows();
  999. if (PEAR::isError($numrows)) {
  1000. return $numrows;
  1001. }
  1002. return $this->rownum < ($numrows - 1);
  1003. }
  1004. // }}}
  1005. // {{{ numRows()
  1006. /**
  1007. * Returns the number of rows in a result object
  1008. *
  1009. * @return mixed MDB2 Error Object or the number of rows
  1010. * @access public
  1011. */
  1012. function numRows()
  1013. {
  1014. $rows = 0;
  1015. $this->result->reset();
  1016. while($this->result->fetchArray()) {
  1017. $rows++;
  1018. }
  1019. $this->result->reset();
  1020. return $rows;
  1021. }
  1022. }
  1023. /**
  1024. * MDB2 SQLite statement driver
  1025. *
  1026. * @package MDB2
  1027. * @category Database
  1028. * @author Lukas Smith <smith@pooteeweet.org>
  1029. */
  1030. class MDB2_Statement_sqlite3 extends MDB2_Statement_Common
  1031. {
  1032. // }}}
  1033. // {{{ function bindValue($parameter, &$value, $type = null)
  1034. private function getParamType($type) {
  1035. switch(strtolower($type)) {
  1036. case 'text':
  1037. return SQLITE3_TEXT;
  1038. case 'boolean':
  1039. case 'integer':
  1040. return SQLITE3_INTEGER;
  1041. case 'float':
  1042. return SQLITE3_FLOAT;
  1043. case 'blob':
  1044. return SQLITE3_BLOB;
  1045. }
  1046. }
  1047. /**
  1048. * Set the value of a parameter of a prepared query.
  1049. *
  1050. * @param int the order number of the parameter in the query
  1051. * statement. The order number of the first parameter is 1.
  1052. * @param mixed value that is meant to be assigned to specified
  1053. * parameter. The type of the value depends on the $type argument.
  1054. * @param string specifies the type of the field
  1055. *
  1056. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1057. *
  1058. * @access public
  1059. */
  1060. function bindValue($parameter, $value, $type = null) {
  1061. if($type) {
  1062. $type=$this->getParamType($type);
  1063. $this->statement->bindValue($parameter, $value, $type);
  1064. }else{
  1065. $this->statement->bindValue($parameter, $value);
  1066. }
  1067. return MDB2_OK;
  1068. }
  1069. /**
  1070. * Bind a variable to a parameter of a prepared query.
  1071. *
  1072. * @param int the order number of the parameter in the query
  1073. * statement. The order number of the first parameter is 1.
  1074. * @param mixed variable that is meant to be bound to specified
  1075. * parameter. The type of the value depends on the $type argument.
  1076. * @param string specifies the type of the field
  1077. *
  1078. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1079. *
  1080. * @access public
  1081. */
  1082. function bindParam($parameter, &$value, $type = null) {
  1083. if($type) {
  1084. $type=$this->getParamType($type);
  1085. $this->statement->bindParam($parameter, $value, $type);
  1086. }else{
  1087. $this->statement->bindParam($parameter, $value);
  1088. }
  1089. return MDB2_OK;
  1090. }
  1091. /**
  1092. * Release resources allocated for the specified prepared query.
  1093. *
  1094. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1095. * @access public
  1096. */
  1097. function free()
  1098. {
  1099. $this->statement->close();
  1100. }
  1101. /**
  1102. * Execute a prepared query statement helper method.
  1103. *
  1104. * @param mixed $result_class string which specifies which result class to use
  1105. * @param mixed $result_wrap_class string which specifies which class to wrap results in
  1106. *
  1107. * @return mixed MDB2_Result or integer (affected rows) on success,
  1108. * a MDB2 error on failure
  1109. * @access private
  1110. */
  1111. function _execute($result_class = true, $result_wrap_class = false) {
  1112. if (is_null($this->statement)) {
  1113. $result =& parent::_execute($result_class, $result_wrap_class);
  1114. return $result;
  1115. }
  1116. $this->db->last_query = $this->query;
  1117. $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values));
  1118. if ($this->db->getOption('disable_query')) {
  1119. $result = $this->is_manip ? 0 : null;
  1120. return $result;
  1121. }
  1122. $connection = $this->db->getConnection();
  1123. if (PEAR::isError($connection)) {
  1124. return $connection;
  1125. }
  1126. $result = $this->statement->execute();
  1127. if ($result==false) {
  1128. $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  1129. 'cant execute statement', __FUNCTION__);
  1130. }
  1131. if ($this->is_manip) {
  1132. $affected_rows = $this->db->_affectedRows($connection, $result);
  1133. return $affected_rows;
  1134. }
  1135. $result = $this->db->_wrapResult($result, $this->result_types,
  1136. $result_class, $result_wrap_class, $this->limit, $this->offset);
  1137. $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
  1138. return $result;
  1139. }
  1140. /**
  1141. * Set the values of multiple a parameter of a prepared query in bulk.
  1142. *
  1143. * @param array specifies all necessary information
  1144. * for bindValue() the array elements must use keys corresponding to
  1145. * the number of the position of the parameter.
  1146. * @param array specifies the types of the fields
  1147. *
  1148. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1149. *
  1150. * @access public
  1151. * @see bindParam()
  1152. */
  1153. function bindValueArray($values, $types = null)
  1154. {
  1155. $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
  1156. $parameters = array_keys($values);
  1157. foreach ($parameters as $key => $parameter) {
  1158. $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
  1159. $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  1160. $err = $this->bindValue($parameter+1, $values[$parameter], $types[$key]);
  1161. $this->db->popExpect();
  1162. $this->db->popErrorHandling();
  1163. if (PEAR::isError($err)) {
  1164. if ($err->getCode() == MDB2_ERROR_NOT_FOUND) {
  1165. //ignore (extra value for missing placeholder)
  1166. continue;
  1167. }
  1168. return $err;
  1169. }
  1170. }
  1171. return MDB2_OK;
  1172. }
  1173. // }}}
  1174. // {{{ function bindParamArray(&$values, $types = null)
  1175. /**
  1176. * Bind the variables of multiple a parameter of a prepared query in bulk.
  1177. *
  1178. * @param array specifies all necessary information
  1179. * for bindParam() the array elements must use keys corresponding to
  1180. * the number of the position of the parameter.
  1181. * @param array specifies the types of the fields
  1182. *
  1183. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1184. *
  1185. * @access public
  1186. * @see bindParam()
  1187. */
  1188. function bindParamArray(&$values, $types = null)
  1189. {
  1190. $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
  1191. $parameters = array_keys($values);
  1192. foreach ($parameters as $key => $parameter) {
  1193. $err = $this->bindParam($parameter+1, $values[$parameter], $types[$key]);
  1194. if (PEAR::isError($err)) {
  1195. return $err;
  1196. }
  1197. }
  1198. return MDB2_OK;
  1199. }
  1200. // }}}
  1201. // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false)
  1202. /**
  1203. * Execute a prepared query statement.
  1204. *
  1205. * @param array specifies all necessary information
  1206. * for bindParam() the array elements must use keys corresponding
  1207. * to the number of the position of the parameter.
  1208. * @param mixed specifies which result class to use
  1209. * @param mixed specifies which class to wrap results in
  1210. *
  1211. * @return mixed MDB2_Result or integer (affected rows) on success,
  1212. * a MDB2 error on failure
  1213. * @access public
  1214. */
  1215. function execute($values = null, $result_class = true, $result_wrap_class = false)
  1216. {
  1217. if (is_null($this->positions)) {
  1218. return $this->db->raiseError(MDB2_ERROR, null, null,
  1219. 'Prepared statement has already been freed', __FUNCTION__);
  1220. }
  1221. $values = (array)$values;
  1222. if (!empty($values)) {
  1223. if(count($this->types)) {
  1224. $types=$this->types;
  1225. }else{
  1226. $types=null;
  1227. }
  1228. $err = $this->bindValueArray($values, $types);
  1229. if (PEAR::isError($err)) {
  1230. return $this->db->raiseError(MDB2_ERROR, null, null,
  1231. 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__);
  1232. }
  1233. }
  1234. $result =$this->_execute($result_class, $result_wrap_class);
  1235. return $result;
  1236. }
  1237. function __destruct() {
  1238. $this->free();
  1239. }
  1240. }