ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
mysql.php
Go to the documentation of this file.
1<?php
2// vim: set et ts=4 sw=4 fdm=marker:
3// +----------------------------------------------------------------------+
4// | PHP versions 4 and 5 |
5// +----------------------------------------------------------------------+
6// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
7// | Stig. S. Bakken, Lukas Smith |
8// | All rights reserved. |
9// +----------------------------------------------------------------------+
10// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
11// | API as well as database abstraction for PHP applications. |
12// | This LICENSE is in the BSD license style. |
13// | |
14// | Redistribution and use in source and binary forms, with or without |
15// | modification, are permitted provided that the following conditions |
16// | are met: |
17// | |
18// | Redistributions of source code must retain the above copyright |
19// | notice, this list of conditions and the following disclaimer. |
20// | |
21// | Redistributions in binary form must reproduce the above copyright |
22// | notice, this list of conditions and the following disclaimer in the |
23// | documentation and/or other materials provided with the distribution. |
24// | |
25// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
26// | Lukas Smith nor the names of his contributors may be used to endorse |
27// | or promote products derived from this software without specific prior|
28// | written permission. |
29// | |
30// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
31// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
32// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
33// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
34// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
35// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
36// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
37// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
38// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
39// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
40// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
41// | POSSIBILITY OF SUCH DAMAGE. |
42// +----------------------------------------------------------------------+
43// | Author: Lukas Smith <smith@pooteeweet.org> |
44// +----------------------------------------------------------------------+
45//
46// $Id: mysql.php,v 1.182 2007/05/02 22:00:08 quipo Exp $
47//
48
57{
58 // {{{ properties
59 var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => '\\', 'escape_pattern' => '\\');
60
61 var $identifier_quoting = array('start' => '`', 'end' => '`', 'escape' => '`');
62
63 var $sql_comments = array(
64 array('start' => '-- ', 'end' => "\n", 'escape' => false),
65 array('start' => '#', 'end' => "\n", 'escape' => false),
66 array('start' => '/*', 'end' => '*/', 'escape' => false),
67 );
68
69 var $start_transaction = false;
70
72 // }}}
73 // {{{ constructor
74
78 function __construct()
79 {
80 parent::__construct();
81
82 $this->phptype = 'mysql';
83 $this->dbsyntax = 'mysql';
84
85 $this->supported['sequences'] = 'emulated';
86 $this->supported['indexes'] = true;
87 $this->supported['affected_rows'] = true;
88 $this->supported['transactions'] = false;
89 $this->supported['savepoints'] = false;
90 $this->supported['summary_functions'] = true;
91 $this->supported['order_by_text'] = true;
92 $this->supported['current_id'] = 'emulated';
93 $this->supported['limit_queries'] = true;
94 $this->supported['LOBs'] = true;
95 $this->supported['replace'] = true;
96 $this->supported['sub_selects'] = 'emulated';
97 $this->supported['auto_increment'] = true;
98 $this->supported['primary_key'] = true;
99 $this->supported['result_introspection'] = true;
100 $this->supported['prepared_statements'] = 'emulated';
101 $this->supported['identifier_quoting'] = true;
102 $this->supported['pattern_escaping'] = true;
103 $this->supported['new_link'] = true;
104
105 $this->options['default_table_type'] = '';
106 }
107
108 // }}}
109 // {{{ errorInfo()
110
118 function errorInfo($error = null)
119 {
120 if ($this->connection) {
121 $native_code = @mysql_errno($this->connection);
122 $native_msg = @mysql_error($this->connection);
123 } else {
124 $native_code = @mysql_errno();
125 $native_msg = @mysql_error();
126 }
127 if (is_null($error)) {
128 static $ecode_map;
129 if (empty($ecode_map)) {
130 $ecode_map = array(
139 1048 => MDB2_ERROR_CONSTRAINT,
140 1049 => MDB2_ERROR_NOSUCHDB,
146 1064 => MDB2_ERROR_SYNTAX,
147 1091 => MDB2_ERROR_NOT_FOUND,
148 1100 => MDB2_ERROR_NOT_LOCKED,
152 1216 => MDB2_ERROR_CONSTRAINT,
153 1217 => MDB2_ERROR_CONSTRAINT,
154 1356 => MDB2_ERROR_DIVZERO,
155 1451 => MDB2_ERROR_CONSTRAINT,
156 1452 => MDB2_ERROR_CONSTRAINT,
157 );
158 }
159 if ($this->options['portability'] & MDB2_PORTABILITY_ERRORS) {
160 $ecode_map[1022] = MDB2_ERROR_CONSTRAINT;
161 $ecode_map[1048] = MDB2_ERROR_CONSTRAINT_NOT_NULL;
162 $ecode_map[1062] = MDB2_ERROR_CONSTRAINT;
163 } else {
164 // Doing this in case mode changes during runtime.
165 $ecode_map[1022] = MDB2_ERROR_ALREADY_EXISTS;
166 $ecode_map[1048] = MDB2_ERROR_CONSTRAINT;
167 $ecode_map[1062] = MDB2_ERROR_ALREADY_EXISTS;
168 }
169 if (isset($ecode_map[$native_code])) {
170 $error = $ecode_map[$native_code];
171 }
172 }
173 return array($error, $native_code, $native_msg);
174 }
175
176 // }}}
177 // {{{ escape()
178
190 function escape($text, $escape_wildcards = false)
191 {
192 if ($escape_wildcards) {
193 $text = $this->escapePattern($text);
194 }
195 $connection = $this->getConnection();
197 return $connection;
198 }
199 $text = @mysql_real_escape_string($text, $connection);
200 return $text;
201 }
202
203 // }}}
204 // {{{
205
214 function beginTransaction($savepoint = null)
215 {
216 $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
217 $this->_getServerCapabilities();
218 if (!is_null($savepoint)) {
219 if (!$this->supports('savepoints')) {
220 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
221 'savepoints are not supported', __FUNCTION__);
222 }
223 if (!$this->in_transaction) {
224 return $this->raiseError(MDB2_ERROR_INVALID, null, null,
225 'savepoint cannot be released when changes are auto committed', __FUNCTION__);
226 }
227 $query = 'SAVEPOINT '.$savepoint;
228 return $this->_doQuery($query, true);
229 } elseif ($this->in_transaction) {
230 return MDB2_OK; //nothing to do
231 }
232 if (!$this->destructor_registered && $this->opened_persistent) {
233 $this->destructor_registered = true;
234 register_shutdown_function('MDB2_closeOpenTransactions');
235 }
236 $query = $this->start_transaction ? 'START TRANSACTION' : 'SET AUTOCOMMIT = 1';
237 $result =& $this->_doQuery($query, true);
238 if (PEAR::isError($result)) {
239 return $result;
240 }
241 $this->in_transaction = true;
242 return MDB2_OK;
243 }
244
245 // }}}
246 // {{{ commit()
247
259 function commit($savepoint = null)
260 {
261 $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
262 if (!$this->in_transaction) {
263 return $this->raiseError(MDB2_ERROR_INVALID, null, null,
264 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
265 }
266 if (!is_null($savepoint)) {
267 if (!$this->supports('savepoints')) {
268 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
269 'savepoints are not supported', __FUNCTION__);
270 }
271 $server_info = $this->getServerVersion();
272 if (version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '5.0.3', '<')) {
273 return MDB2_OK;
274 }
275 $query = 'RELEASE SAVEPOINT '.$savepoint;
276 return $this->_doQuery($query, true);
277 }
278
279 if (!$this->supports('transactions')) {
280 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
281 'transactions are not supported', __FUNCTION__);
282 }
283
284 $result =& $this->_doQuery('COMMIT', true);
285 if (PEAR::isError($result)) {
286 return $result;
287 }
288 if (!$this->start_transaction) {
289 $query = 'SET AUTOCOMMIT = 0';
290 $result =& $this->_doQuery($query, true);
291 if (PEAR::isError($result)) {
292 return $result;
293 }
294 }
295 $this->in_transaction = false;
296 return MDB2_OK;
297 }
298
299 // }}}
300 // {{{ rollback()
301
313 function rollback($savepoint = null)
314 {
315 $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
316 if (!$this->in_transaction) {
317 return $this->raiseError(MDB2_ERROR_INVALID, null, null,
318 'rollback cannot be done changes are auto committed', __FUNCTION__);
319 }
320 if (!is_null($savepoint)) {
321 if (!$this->supports('savepoints')) {
322 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
323 'savepoints are not supported', __FUNCTION__);
324 }
325 $query = 'ROLLBACK TO SAVEPOINT '.$savepoint;
326 return $this->_doQuery($query, true);
327 }
328
329 $query = 'ROLLBACK';
330 $result =& $this->_doQuery($query, true);
331 if (PEAR::isError($result)) {
332 return $result;
333 }
334 if (!$this->start_transaction) {
335 $query = 'SET AUTOCOMMIT = 0';
336 $result =& $this->_doQuery($query, true);
337 if (PEAR::isError($result)) {
338 return $result;
339 }
340 }
341 $this->in_transaction = false;
342 return MDB2_OK;
343 }
344
345 // }}}
346 // {{{ function setTransactionIsolation()
347
361 function setTransactionIsolation($isolation)
362 {
363 $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
364 if (!$this->supports('transactions')) {
365 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
366 'transactions are not supported', __FUNCTION__);
367 }
368 switch ($isolation) {
369 case 'READ UNCOMMITTED':
370 case 'READ COMMITTED':
371 case 'REPEATABLE READ':
372 case 'SERIALIZABLE':
373 break;
374 default:
375 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
376 'isolation level is not supported: '.$isolation, __FUNCTION__);
377 }
378
379 $query = "SET SESSION TRANSACTION ISOLATION LEVEL $isolation";
380 return $this->_doQuery($query, true);
381 }
382
383 // }}}
384 // {{{ connect()
385
391 function connect()
392 {
393 if (is_resource($this->connection)) {
394 // Performance fix: == is much faster than array_diff
395 #if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
396 if($this->connected_dsn == $this->dsn
397 && $this->opened_persistent == $this->options['persistent']
398 && $this->connected_database_name == $this->database_name
399 ) {
400 return MDB2_OK;
401 }
402 $this->disconnect(false);
403 }
404
405 if (!PEAR::loadExtension($this->phptype)) {
406 return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
407 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
408 }
409
410 $params = array();
411 if ($this->dsn['protocol'] && $this->dsn['protocol'] == 'unix') {
412 $params[0] = ':' . $this->dsn['socket'];
413 } else {
414 $params[0] = $this->dsn['hostspec'] ? $this->dsn['hostspec']
415 : 'localhost';
416 if ($this->dsn['port']) {
417 $params[0].= ':' . $this->dsn['port'];
418 }
419 }
420 $params[] = $this->dsn['username'] ? $this->dsn['username'] : null;
421 $params[] = $this->dsn['password'] ? $this->dsn['password'] : null;
422 if (!$this->options['persistent']) {
423 if (isset($this->dsn['new_link'])
424 && ($this->dsn['new_link'] == 'true' || $this->dsn['new_link'] === true)
425 ) {
426 $params[] = true;
427 } else {
428 $params[] = false;
429 }
430 }
431 if (version_compare(phpversion(), '4.3.0', '>=')) {
432 $params[] = isset($this->dsn['client_flags'])
433 ? $this->dsn['client_flags'] : null;
434 }
435 $connect_function = $this->options['persistent'] ? 'mysql_pconnect' : 'mysql_connect';
436
437 $connection = @call_user_func_array($connect_function, $params);
438 if (!$connection) {
439 if (($err = @mysql_error()) != '') {
440 return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
441 $err, __FUNCTION__);
442 } else {
443 return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
444 'unable to establish a connection', __FUNCTION__);
445 }
446 }
447
448 if (!empty($this->dsn['charset'])) {
449 $result = $this->setCharset($this->dsn['charset'], $connection);
450 if (PEAR::isError($result)) {
451 return $result;
452 }
453 }
454
455 $this->connection = $connection;
456 $this->connected_dsn = $this->dsn;
457 $this->connected_database_name = '';
458 $this->opened_persistent = $this->options['persistent'];
459 $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
460
461 if ($this->database_name) {
462 if ($this->database_name != $this->connected_database_name) {
463 if (!@mysql_select_db($this->database_name, $connection)) {
464 $err = $this->raiseError(null, null, null,
465 'Could not select the database: '.$this->database_name, __FUNCTION__);
466 return $err;
467 }
468 $this->connected_database_name = $this->database_name;
469 }
470 }
471
472 $this->supported['transactions'] = $this->options['use_transactions'];
473 if ($this->options['default_table_type']) {
474 switch (strtoupper($this->options['default_table_type'])) {
475 case 'BLACKHOLE':
476 case 'MEMORY':
477 case 'ARCHIVE':
478 case 'CSV':
479 case 'HEAP':
480 case 'ISAM':
481 case 'MERGE':
482 case 'MRG_ISAM':
483 case 'ISAM':
484 case 'MRG_MYISAM':
485 case 'MYISAM':
486 $this->supported['transactions'] = false;
487 $this->warnings[] = $this->options['default_table_type'] .
488 ' is not a supported default table type';
489 break;
490 }
491 }
492
493 $this->_getServerCapabilities();
494
495 return MDB2_OK;
496 }
497
498 // }}}
499 // {{{ setCharset()
500
509 function setCharset($charset, $connection = null)
510 {
511 if (is_null($connection)) {
512 $connection = $this->getConnection();
514 return $connection;
515 }
516 }
517 $query = "SET NAMES '".mysql_real_escape_string($charset, $connection)."'";
518 return $this->_doQuery($query, true, $connection);
519 }
520
521 // }}}
522 // {{{ disconnect()
523
533 function disconnect($force = true)
534 {
535 if (is_resource($this->connection)) {
536 if ($this->in_transaction) {
539 $persistent = $this->options['persistent'];
540 $this->dsn = $this->connected_dsn;
541 $this->database_name = $this->connected_database_name;
542 $this->options['persistent'] = $this->opened_persistent;
543 $this->rollback();
544 $this->dsn = $dsn;
545 $this->database_name = $database_name;
546 $this->options['persistent'] = $persistent;
547 }
548
549 if (!$this->opened_persistent || $force) {
550 @mysql_close($this->connection);
551 }
552 }
553 return parent::disconnect($force);
554 }
555
556 // }}}
557 // {{{ _doQuery()
558
568 function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
569 {
570 $this->last_query = $query;
571 $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
572 if ($result) {
573 if (PEAR::isError($result)) {
574 return $result;
575 }
576 $query = $result;
577 }
578 if ($this->options['disable_query']) {
579 $result = $is_manip ? 0 : null;
580 return $result;
581 }
582
583 if (is_null($connection)) {
584 $connection = $this->getConnection();
586 return $connection;
587 }
588 }
589 if (is_null($database_name)) {
591 }
592
593 if ($database_name) {
594 if ($database_name != $this->connected_database_name) {
595 if (!@mysql_select_db($database_name, $connection)) {
596 $err = $this->raiseError(null, null, null,
597 'Could not select the database: '.$database_name, __FUNCTION__);
598 return $err;
599 }
600 $this->connected_database_name = $database_name;
601 }
602 }
603
604 $function = $this->options['result_buffering']
605 ? 'mysql_query' : 'mysql_unbuffered_query';
606 $result = @$function($query, $connection);
607 if (!$result) {
608 $err =& $this->raiseError(null, null, null,
609 'Could not execute statement', __FUNCTION__);
610 return $err;
611 }
612
613 $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
614 return $result;
615 }
616
617 // }}}
618 // {{{ _affectedRows()
619
629 {
630 if (is_null($connection)) {
631 $connection = $this->getConnection();
633 return $connection;
634 }
635 }
636 return @mysql_affected_rows($connection);
637 }
638
639 // }}}
640 // {{{ _modifyQuery()
641
652 function _modifyQuery($query, $is_manip, $limit, $offset)
653 {
654 if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) {
655 // "DELETE FROM table" gives 0 affected rows in MySQL.
656 // This little hack lets you know how many rows were deleted.
657 if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
658 $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
659 'DELETE FROM \1 WHERE 1=1', $query);
660 }
661 }
662 if ($limit > 0
663 && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\‍)]*)?$/i', $query)
664 ) {
665 $query = rtrim($query);
666 if (substr($query, -1) == ';') {
667 $query = substr($query, 0, -1);
668 }
669
670 // LIMIT doesn't always come last in the query
671 // @see http://dev.mysql.com/doc/refman/5.0/en/select.html
672 $after = '';
673 if (preg_match('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', $query, $matches)) {
674 $after = $matches[0];
675 $query = preg_replace('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', '', $query);
676 } elseif (preg_match('/(\s+FOR\s+UPDATE\s*)$/i', $query, $matches)) {
677 $after = $matches[0];
678 $query = preg_replace('/(\s+FOR\s+UPDATE\s*)$/im', '', $query);
679 } elseif (preg_match('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', $query, $matches)) {
680 $after = $matches[0];
681 $query = preg_replace('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', '', $query);
682 }
683
684 if ($is_manip) {
685 return $query . " LIMIT $limit" . $after;
686 } else {
687 return $query . " LIMIT $offset, $limit" . $after;
688 }
689 }
690 return $query;
691 }
692
693 // }}}
694 // {{{ getServerVersion()
695
703 function getServerVersion($native = false)
704 {
705 $connection = $this->getConnection();
707 return $connection;
708 }
709 if ($this->connected_server_info) {
710 $server_info = $this->connected_server_info;
711 } else {
712 $server_info = @mysql_get_server_info($connection);
713 }
714 if (!$server_info) {
715 return $this->raiseError(null, null, null,
716 'Could not get server information', __FUNCTION__);
717 }
718 // cache server_info
719 $this->connected_server_info = $server_info;
720 if (!$native) {
721 $tmp = explode('.', $server_info, 3);
722 if (isset($tmp[2]) && strpos($tmp[2], '-')) {
723 $tmp2 = explode('-', @$tmp[2], 2);
724 } else {
725 $tmp2[0] = isset($tmp[2]) ? $tmp[2] : null;
726 $tmp2[1] = null;
727 }
728 $server_info = array(
729 'major' => isset($tmp[0]) ? $tmp[0] : null,
730 'minor' => isset($tmp[1]) ? $tmp[1] : null,
731 'patch' => $tmp2[0],
732 'extra' => $tmp2[1],
733 'native' => $server_info,
734 );
735 }
736 return $server_info;
737 }
738
739 // }}}
740 // {{{ _getServerCapabilities()
741
749 {
750 static $already_checked = false;
751 if (!$already_checked) {
752 $already_checked = true;
753
754 //set defaults
755 $this->supported['sub_selects'] = 'emulated';
756 $this->supported['prepared_statements'] = 'emulated';
757 $this->start_transaction = false;
758 $this->varchar_max_length = 255;
759
760 $server_info = $this->getServerVersion();
761 if (is_array($server_info)) {
762 if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.1.0', '<')) {
763 $this->supported['sub_selects'] = true;
764 $this->supported['prepared_statements'] = true;
765 }
766
767 if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.0.14', '<')
768 || !version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.1.1', '<')
769 ) {
770 $this->supported['savepoints'] = true;
771 }
772
773 if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.0.11', '<')) {
774 $this->start_transaction = true;
775 }
776
777 if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '5.0.3', '<')) {
778 $this->varchar_max_length = 65532;
779 }
780 }
781 }
782 }
783
784 // }}}
785 // {{{ function _skipUserDefinedVariable($query, $position)
786
799 function _skipUserDefinedVariable($query, $position)
800 {
801 $found = strpos(strrev(substr($query, 0, $position)), '@');
802 if ($found === false) {
803 return $position;
804 }
805 $pos = strlen($query) - strlen(substr($query, $position)) - $found - 1;
806 $substring = substr($query, $pos, $position - $pos + 2);
807 if (preg_match('/^@\w+:=$/', $substring)) {
808 return $position + 1; //found an user defined variable: skip it
809 }
810 return $position;
811 }
812
813 // }}}
814 // {{{ prepare()
815
836 function &prepare($query, $types = null, $result_types = null, $lobs = array())
837 {
838 if ($this->options['emulate_prepared']
839 || $this->supported['prepared_statements'] !== true
840 ) {
841 $obj =& parent::prepare($query, $types, $result_types, $lobs);
842 return $obj;
843 }
844 $is_manip = ($result_types === MDB2_PREPARE_MANIP);
847 $this->offset = $this->limit = 0;
848 $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
849 $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
850 if ($result) {
851 if (PEAR::isError($result)) {
852 return $result;
853 }
854 $query = $result;
855 }
856 $placeholder_type_guess = $placeholder_type = null;
857 $question = '?';
858 $colon = ':';
859 $positions = array();
860 $position = 0;
861 while ($position < strlen($query)) {
862 $q_position = strpos($query, $question, $position);
863 $c_position = strpos($query, $colon, $position);
864 if ($q_position && $c_position) {
865 $p_position = min($q_position, $c_position);
866 } elseif ($q_position) {
867 $p_position = $q_position;
868 } elseif ($c_position) {
869 $p_position = $c_position;
870 } else {
871 break;
872 }
873 if (is_null($placeholder_type)) {
874 $placeholder_type_guess = $query[$p_position];
875 }
876
877 $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
878 if (PEAR::isError($new_pos)) {
879 return $new_pos;
880 }
881 if ($new_pos != $position) {
882 $position = $new_pos;
883 continue; //evaluate again starting from the new position
884 }
885
886 if ($query[$position] == $placeholder_type_guess) {
887 if (is_null($placeholder_type)) {
888 $placeholder_type = $query[$p_position];
889 $question = $colon = $placeholder_type;
890 }
891 if ($placeholder_type == ':') {
892 //make sure this is not part of an user defined variable
893 $new_pos = $this->_skipUserDefinedVariable($query, $position);
894 if ($new_pos != $position) {
895 $position = $new_pos;
896 continue; //evaluate again starting from the new position
897 }
898 $parameter = preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/si', '\\1', $query);
899 if ($parameter === '') {
900 $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
901 'named parameter with an empty name', __FUNCTION__);
902 return $err;
903 }
904 $positions[$p_position] = $parameter;
905 $query = substr_replace($query, '?', $position, strlen($parameter)+1);
906 } else {
907 $positions[$p_position] = count($positions);
908 }
909 $position = $p_position + 1;
910 } else {
911 $position = $p_position;
912 }
913 }
914 $connection = $this->getConnection();
916 return $connection;
917 }
918 $statement_name = sprintf($this->options['statement_format'], $this->phptype, md5(time() + rand()));
919 $query = "PREPARE $statement_name FROM ".$this->quote($query, 'text');
920 $statement =& $this->_doQuery($query, true, $connection);
921 if (PEAR::isError($statement)) {
922 return $statement;
923 }
924
925 $class_name = 'MDB2_Statement_'.$this->phptype;
926 $obj =& new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
927 $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
928 return $obj;
929 }
930
931 // }}}
932 // {{{ replace()
933
998 function replace($table, $fields)
999 {
1000 $count = count($fields);
1001 $query = $values = '';
1002 $keys = $colnum = 0;
1003 for (reset($fields); $colnum < $count; next($fields), $colnum++) {
1004 $name = key($fields);
1005 if ($colnum > 0) {
1006 $query .= ',';
1007 $values.= ',';
1008 }
1009 $query.= $name;
1010 if (isset($fields[$name]['null']) && $fields[$name]['null']) {
1011 $value = 'NULL';
1012 } else {
1013 $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
1014 $value = $this->quote($fields[$name]['value'], $type);
1015 }
1016 $values.= $value;
1017 if (isset($fields[$name]['key']) && $fields[$name]['key']) {
1018 if ($value === 'NULL') {
1019 return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
1020 'key value '.$name.' may not be NULL', __FUNCTION__);
1021 }
1022 $keys++;
1023 }
1024 }
1025 if ($keys == 0) {
1026 return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
1027 'not specified which fields are keys', __FUNCTION__);
1028 }
1029
1030 $connection = $this->getConnection();
1032 return $connection;
1033 }
1034
1035 $query = "REPLACE INTO $table ($query) VALUES ($values)";
1036 $result =& $this->_doQuery($query, true, $connection);
1037 if (PEAR::isError($result)) {
1038 return $result;
1039 }
1040 return $this->_affectedRows($connection, $result);
1041 }
1042
1043 // }}}
1044 // {{{ nextID()
1045
1057 function nextID($seq_name, $ondemand = true)
1058 {
1059 $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
1060 $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
1061 $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)";
1063 $result =& $this->_doQuery($query, true);
1064 $this->popExpect();
1065 if (PEAR::isError($result)) {
1066 if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
1067 $this->loadModule('Manager', null, true);
1068 $result = $this->manager->createSequence($seq_name);
1069 if (PEAR::isError($result)) {
1070 return $this->raiseError($result, null, null,
1071 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
1072 } else {
1073 return $this->nextID($seq_name, false);
1074 }
1075 }
1076 return $result;
1077 }
1078 $value = $this->lastInsertID();
1079 if (is_numeric($value)) {
1080 $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
1081 $result =& $this->_doQuery($query, true);
1082 if (PEAR::isError($result)) {
1083 $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
1084 }
1085 }
1086 return $value;
1087 }
1088
1089 // }}}
1090 // {{{ lastInsertID()
1091
1101 function lastInsertID($table = null, $field = null)
1102 {
1103 // not using mysql_insert_id() due to http://pear.php.net/bugs/bug.php?id=8051
1104 return $this->queryOne('SELECT LAST_INSERT_ID()', 'integer');
1105 }
1106
1107 // }}}
1108 // {{{ currID()
1109
1117 function currID($seq_name)
1118 {
1119 $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
1120 $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
1121 $query = "SELECT MAX($seqcol_name) FROM $sequence_name";
1122 return $this->queryOne($query, 'integer');
1123 }
1124}
1125
1134{
1135 // }}}
1136 // {{{ fetchRow()
1137
1146 function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
1147 {
1148 if (!is_null($rownum)) {
1149 $seek = $this->seek($rownum);
1150 if (PEAR::isError($seek)) {
1151 return $seek;
1152 }
1153 }
1154 if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
1155 $fetchmode = $this->db->fetchmode;
1156 }
1157 if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
1158 $row = @mysql_fetch_assoc($this->result);
1159 if (is_array($row)
1160 && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
1161 ) {
1162 $row = array_change_key_case($row, $this->db->options['field_case']);
1163 }
1164 } else {
1165 $row = @mysql_fetch_row($this->result);
1166 }
1167
1168 if (!$row) {
1169 if ($this->result === false) {
1170 $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1171 'resultset has already been freed', __FUNCTION__);
1172 return $err;
1173 }
1174 $null = null;
1175 return $null;
1176 }
1177 $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
1178 if ($mode) {
1179 $this->db->_fixResultArrayValues($row, $mode);
1180 }
1181 if (!empty($this->types)) {
1182 $row = $this->db->datatype->convertResultRow($this->types, $row, false);
1183 }
1184 if (!empty($this->values)) {
1185 $this->_assignBindColumns($row);
1186 }
1187 if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
1188 $object_class = $this->db->options['fetch_class'];
1189 if ($object_class == 'stdClass') {
1190 $row = (object) $row;
1191 } else {
1192 $row = &new $object_class($row);
1193 }
1194 }
1195 ++$this->rownum;
1196 return $row;
1197 }
1198
1199 // }}}
1200 // {{{ _getColumnNames()
1201
1212 {
1213 $columns = array();
1214 $numcols = $this->numCols();
1215 if (PEAR::isError($numcols)) {
1216 return $numcols;
1217 }
1218 for ($column = 0; $column < $numcols; $column++) {
1219 $column_name = @mysql_field_name($this->result, $column);
1220 $columns[$column_name] = $column;
1221 }
1222 if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
1223 $columns = array_change_key_case($columns, $this->db->options['field_case']);
1224 }
1225 return $columns;
1226 }
1227
1228 // }}}
1229 // {{{ numCols()
1230
1238 function numCols()
1239 {
1240 $cols = @mysql_num_fields($this->result);
1241 if (is_null($cols)) {
1242 if ($this->result === false) {
1243 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1244 'resultset has already been freed', __FUNCTION__);
1245 } elseif (is_null($this->result)) {
1246 return count($this->types);
1247 }
1248 return $this->db->raiseError(null, null, null,
1249 'Could not get column count', __FUNCTION__);
1250 }
1251 return $cols;
1252 }
1253
1254 // }}}
1255 // {{{ free()
1256
1263 function free()
1264 {
1265 if (is_resource($this->result) && $this->db->connection) {
1266 $free = @mysql_free_result($this->result);
1267 if ($free === false) {
1268 return $this->db->raiseError(null, null, null,
1269 'Could not free result', __FUNCTION__);
1270 }
1271 }
1272 $this->result = false;
1273 return MDB2_OK;
1274 }
1275}
1276
1285{
1286 // }}}
1287 // {{{ seek()
1288
1296 function seek($rownum = 0)
1297 {
1298 if ($this->rownum != ($rownum - 1) && !@mysql_data_seek($this->result, $rownum)) {
1299 if ($this->result === false) {
1300 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1301 'resultset has already been freed', __FUNCTION__);
1302 } elseif (is_null($this->result)) {
1303 return MDB2_OK;
1304 }
1305 return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
1306 'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
1307 }
1308 $this->rownum = $rownum - 1;
1309 return MDB2_OK;
1310 }
1311
1312 // }}}
1313 // {{{ valid()
1314
1321 function valid()
1322 {
1323 $numrows = $this->numRows();
1324 if (PEAR::isError($numrows)) {
1325 return $numrows;
1326 }
1327 return $this->rownum < ($numrows - 1);
1328 }
1329
1330 // }}}
1331 // {{{ numRows()
1332
1339 function numRows()
1340 {
1341 $rows = @mysql_num_rows($this->result);
1342 if (is_null($rows)) {
1343 if ($this->result === false) {
1344 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1345 'resultset has already been freed', __FUNCTION__);
1346 } elseif (is_null($this->result)) {
1347 return 0;
1348 }
1349 return $this->db->raiseError(null, null, null,
1350 'Could not get row count', __FUNCTION__);
1351 }
1352 return $rows;
1353 }
1354}
1355
1364{
1365 // {{{ _execute()
1366
1375 function &_execute($result_class = true, $result_wrap_class = false)
1376 {
1377 if (is_null($this->statement)) {
1378 $result =& parent::_execute($result_class, $result_wrap_class);
1379 return $result;
1380 }
1381 $this->db->last_query = $this->query;
1382 $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values));
1383 if ($this->db->getOption('disable_query')) {
1384 $result = $this->is_manip ? 0 : null;
1385 return $result;
1386 }
1387
1388 $connection = $this->db->getConnection();
1389 if (PEAR::isError($connection)) {
1390 return $connection;
1391 }
1392
1393 $query = 'EXECUTE '.$this->statement;
1394 if (!empty($this->positions)) {
1395 $parameters = array();
1396 foreach ($this->positions as $parameter) {
1397 if (!array_key_exists($parameter, $this->values)) {
1398 return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
1399 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
1400 }
1401 $value = $this->values[$parameter];
1402 $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
1403 if (is_resource($value) || $type == 'clob' || $type == 'blob') {
1404 if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
1405 if ($match[1] == 'file://') {
1406 $value = $match[2];
1407 }
1408 $value = @fopen($value, 'r');
1409 $close = true;
1410 }
1411 if (is_resource($value)) {
1412 $data = '';
1413 while (!@feof($value)) {
1414 $data.= @fread($value, $this->db->options['lob_buffer_length']);
1415 }
1416 if ($close) {
1417 @fclose($value);
1418 }
1419 $value = $data;
1420 }
1421 }
1422 $quoted = $this->db->quote($value, $type);
1423 if (PEAR::isError($quoted)) {
1424 return $quoted;
1425 }
1426 $param_query = 'SET @'.$parameter.' = '.$quoted;
1427 $result = $this->db->_doQuery($param_query, true, $connection);
1428 if (PEAR::isError($result)) {
1429 return $result;
1430 }
1431 }
1432 $query.= ' USING @'.implode(', @', array_values($this->positions));
1433 }
1434
1435 $result = $this->db->_doQuery($query, $this->is_manip, $connection);
1436 if (PEAR::isError($result)) {
1437 return $result;
1438 }
1439
1440 if ($this->is_manip) {
1441 $affected_rows = $this->db->_affectedRows($connection, $result);
1442 return $affected_rows;
1443 }
1444
1445 $result =& $this->db->_wrapResult($result, $this->result_types,
1446 $result_class, $result_wrap_class, $this->limit, $this->offset);
1447 $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
1448 return $result;
1449 }
1450
1451 // }}}
1452 // {{{ free()
1453
1460 function free()
1461 {
1462 if (is_null($this->positions)) {
1463 return $this->db->raiseError(MDB2_ERROR, null, null,
1464 'Prepared statement has already been freed', __FUNCTION__);
1465 }
1466 $result = MDB2_OK;
1467
1468 if (!is_null($this->statement)) {
1469 $connection = $this->db->getConnection();
1470 if (PEAR::isError($connection)) {
1471 return $connection;
1472 }
1473 $query = 'DEALLOCATE PREPARE '.$this->statement;
1474 $result = $this->db->_doQuery($query, true, $connection);
1475 }
1476
1477 parent::free();
1478 return $result;
1479 }
1480}
1481?>
$result
const MDB2_ERROR_ACCESS_VIOLATION
Definition: MDB2.php:94
const MDB2_ERROR_UNSUPPORTED
Definition: MDB2.php:73
const MDB2_ERROR_NODBSELECTED
Definition: MDB2.php:81
const MDB2_FETCHMODE_DEFAULT
This is a special constant that tells MDB2 the user hasn't specified any particular get mode,...
Definition: MDB2.php:119
const MDB2_ERROR_INVALID
Definition: MDB2.php:75
const MDB2_ERROR_CONSTRAINT_NOT_NULL
Definition: MDB2.php:96
const MDB2_OK
The method mapErrorCode in each MDB2_dbtype implementation maps native error codes to one of these.
Definition: MDB2.php:67
const MDB2_PORTABILITY_FIX_CASE
Portability: convert names of tables and fields to case defined in the "field_case" option when using...
Definition: MDB2.php:158
const MDB2_ERROR_SYNTAX
Definition: MDB2.php:69
const MDB2_ERROR_NOT_LOCKED
Definition: MDB2.php:88
const MDB2_ERROR_NOT_FOUND
Definition: MDB2.php:71
const MDB2_ERROR_VALUE_COUNT_ON_ROW
Definition: MDB2.php:89
const MDB2_ERROR_CANNOT_CREATE
Definition: MDB2.php:82
const MDB2_PORTABILITY_ERRORS
Portability: makes certain error messages in certain drivers compatible with those from other DBMS's.
Definition: MDB2.php:191
const MDB2_PREPARE_MANIP
These are just helper constants to more verbosely express parameters to prepare()
Definition: MDB2.php:109
const MDB2_ERROR_ALREADY_EXISTS
Definition: MDB2.php:72
const MDB2_PORTABILITY_EMPTY_TO_NULL
Portability: convert empty values to null strings in data output by query*() and fetch*().
Definition: MDB2.php:198
const MDB2_ERROR_DIVZERO
Definition: MDB2.php:80
const MDB2_ERROR_CONSTRAINT
Definition: MDB2.php:70
const MDB2_FETCHMODE_OBJECT
Column data as object properties.
Definition: MDB2.php:134
const MDB2_ERROR_NOSUCHFIELD
Definition: MDB2.php:86
const MDB2_ERROR_NEED_MORE_DATA
Definition: MDB2.php:87
const MDB2_ERROR
Definition: MDB2.php:68
const MDB2_ERROR_NOSUCHTABLE
Definition: MDB2.php:85
const MDB2_ERROR_CONNECT_FAILED
Definition: MDB2.php:91
const MDB2_PORTABILITY_DELETE_COUNT
Portability: force reporting the number of rows deleted.
Definition: MDB2.php:170
const MDB2_ERROR_NOSUCHDB
Definition: MDB2.php:93
const MDB2_ERROR_CANNOT_REPLACE
Definition: MDB2.php:95
const MDB2_FETCHMODE_ASSOC
Column data indexed by column names.
Definition: MDB2.php:129
const MDB2_ERROR_CANNOT_DROP
Definition: MDB2.php:84
if(! $in) $columns
Definition: Utf8Test.php:46
numRows()
Returns the number of rows in a result object.
Definition: mysql.php:1339
seek($rownum=0)
Seek to a specific row in a result set.
Definition: mysql.php:1296
valid()
Check if the end of the result set has been reached.
Definition: mysql.php:1321
getConnection()
Returns a native connection.
Definition: MDB2.php:1733
debug($message, $scope='', $context=array())
set a debug message
Definition: MDB2.php:1582
queryOne($query, $type=null, $colnum=0)
Execute the specified query, fetch the value from the first column of the first row of the result set...
Definition: MDB2.php:3203
getSequenceName($sqn)
adds sequence name formatting to a sequence name
Definition: MDB2.php:3102
supports($feature)
Tell whether a DB implementation or its backend extension supports a given feature.
Definition: MDB2.php:3081
& loadModule($module, $property=null, $phptype_specific=null)
loads a module
Definition: MDB2.php:1840
quoteIdentifier($str, $check_option=false)
Quote a string so it can be safely used as a table or column name.
Definition: MDB2.php:1700
escapePattern($text)
Quotes pattern (% and _) characters in a string)
Definition: MDB2.php:1652
_skipDelimitedStrings($query, $position, $p_position)
Utility method, used by prepare() to avoid replacing placeholders within delimited strings.
Definition: MDB2.php:2963
quote($value, $type=null, $quote=true, $escape_wildcards=false)
Convert a text value into a DBMS specific format that is suitable to compose query statements.
Definition: MDB2.php:3009
& raiseError($code=null, $mode=null, $options=null, $userinfo=null, $method=null)
This method is used to communicate an error and invoke error callbacks etc.
Definition: MDB2.php:1407
commit($savepoint=null)
Commit the database changes done during a transaction that is in progress or release a savepoint.
Definition: mysql.php:259
rollback($savepoint=null)
Cancel any database changes done during a transaction or since a specific savepoint that is in progre...
Definition: mysql.php:313
connect()
Connect to the database.
Definition: mysql.php:391
currID($seq_name)
Returns the current id of a sequence.
Definition: mysql.php:1117
escape($text, $escape_wildcards=false)
Quotes a string so it can be safely used in a query.
Definition: mysql.php:190
& _doQuery($query, $is_manip=false, $connection=null, $database_name=null)
Execute a query.
Definition: mysql.php:568
_getServerCapabilities()
Fetch some information about the server capabilities (transactions, subselects, prepared statements,...
Definition: mysql.php:748
lastInsertID($table=null, $field=null)
Returns the autoincrement ID if supported or $id or fetches the current ID in a sequence called: $tab...
Definition: mysql.php:1101
_affectedRows($connection, $result=null)
Returns the number of rows affected.
Definition: mysql.php:628
beginTransaction($savepoint=null)
Start a transaction or set a savepoint.
Definition: mysql.php:214
setCharset($charset, $connection=null)
Set the charset on the current connection.
Definition: mysql.php:509
__construct()
Constructor.
Definition: mysql.php:78
setTransactionIsolation($isolation)
Set the transacton isolation level.
Definition: mysql.php:361
_modifyQuery($query, $is_manip, $limit, $offset)
Changes a query string for various DBMS specific reasons.
Definition: mysql.php:652
& prepare($query, $types=null, $result_types=null, $lobs=array())
Prepares a query for multiple execution with execute().
Definition: mysql.php:836
_skipUserDefinedVariable($query, $position)
Utility method, used by prepare() to avoid misinterpreting MySQL user defined variables (SELECT @x:=5...
Definition: mysql.php:799
replace($table, $fields)
Execute a SQL REPLACE query.
Definition: mysql.php:998
errorInfo($error=null)
This method is used to collect information about an error.
Definition: mysql.php:118
disconnect($force=true)
Log out and disconnect from the database.
Definition: mysql.php:533
getServerVersion($native=false)
return version information about the server
Definition: mysql.php:703
nextID($seq_name, $ondemand=true)
Returns the next free id of a sequence.
Definition: mysql.php:1057
_assignBindColumns($row)
Bind a variable to a value in the result row.
Definition: MDB2.php:3786
seek($rownum=0)
Seek to a specific row in a result set.
Definition: MDB2.php:3432
_getColumnNames()
Retrieve the names of columns returned by the DBMS in a query result.
Definition: mysql.php:1211
free()
Free the internal resources associated with result.
Definition: mysql.php:1263
numCols()
Count the number of columns returned by the DBMS in a query result.
Definition: mysql.php:1238
& fetchRow($fetchmode=MDB2_FETCHMODE_DEFAULT, $rownum=null)
Fetch a row and insert the data into an existing array.
Definition: mysql.php:1146
& _execute($result_class=true, $result_wrap_class=false)
Execute a prepared query statement helper method.
Definition: mysql.php:1375
free()
Release resources allocated for the specified prepared query.
Definition: mysql.php:1460
loadExtension($ext)
OS independant PHP extension load.
Definition: PEAR.php:745
popExpect()
This method pops one element off the expected error codes stack.
Definition: PEAR.php:409
expectError($code=' *')
This method is used to tell which errors you expect to get.
Definition: PEAR.php:390
isError($data, $code=null)
Tell whether a value is a PEAR error.
Definition: PEAR.php:279
$data
$text
$params
Definition: example_049.php:96