commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-old / civicrm / packages / DB / mysql.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * The PEAR DB driver for PHP's mysql extension
7 * for interacting with MySQL databases
8 *
9 * PHP versions 4 and 5
10 *
11 * LICENSE: This source file is subject to version 3.0 of the PHP license
12 * that is available through the world-wide-web at the following URI:
13 * http://www.php.net/license/3_0.txt. If you did not receive a copy of
14 * the PHP License and are unable to obtain it through the web, please
15 * send a note to license@php.net so we can mail you a copy immediately.
16 *
17 * @category Database
18 * @package DB
19 * @author Stig Bakken <ssb@php.net>
20 * @author Daniel Convissor <danielc@php.net>
21 * @copyright 1997-2007 The PHP Group
22 * @license http://www.php.net/license/3_0.txt PHP License 3.0
23 * @version CVS: $Id: mysql.php,v 1.126 2007/09/21 13:32:52 aharvey Exp $
24 * @link http://pear.php.net/package/DB
25 */
26
27 /**
28 * Obtain the DB_common class so it can be extended from
29 */
30 require_once 'DB/common.php';
31
32 /**
33 * The methods PEAR DB uses to interact with PHP's mysql extension
34 * for interacting with MySQL databases
35 *
36 * These methods overload the ones declared in DB_common.
37 *
38 * @category Database
39 * @package DB
40 * @author Stig Bakken <ssb@php.net>
41 * @author Daniel Convissor <danielc@php.net>
42 * @copyright 1997-2007 The PHP Group
43 * @license http://www.php.net/license/3_0.txt PHP License 3.0
44 * @version Release: 1.7.13
45 * @link http://pear.php.net/package/DB
46 */
47 class DB_mysql extends DB_common
48 {
49 // {{{ properties
50
51 /**
52 * The DB driver type (mysql, oci8, odbc, etc.)
53 * @var string
54 */
55 var $phptype = 'mysql';
56
57 /**
58 * The database syntax variant to be used (db2, access, etc.), if any
59 * @var string
60 */
61 var $dbsyntax = 'mysql';
62
63 /**
64 * The capabilities of this DB implementation
65 *
66 * The 'new_link' element contains the PHP version that first provided
67 * new_link support for this DBMS. Contains false if it's unsupported.
68 *
69 * Meaning of the 'limit' element:
70 * + 'emulate' = emulate with fetch row by number
71 * + 'alter' = alter the query
72 * + false = skip rows
73 *
74 * @var array
75 */
76 var $features = array(
77 'limit' => 'alter',
78 'new_link' => '4.2.0',
79 'numrows' => true,
80 'pconnect' => true,
81 'prepare' => false,
82 'ssl' => false,
83 'transactions' => true,
84 );
85
86 /**
87 * A mapping of native error codes to DB error codes
88 * @var array
89 */
90 var $errorcode_map = array(
91 1004 => DB_ERROR_CANNOT_CREATE,
92 1005 => DB_ERROR_CANNOT_CREATE,
93 1006 => DB_ERROR_CANNOT_CREATE,
94 1007 => DB_ERROR_ALREADY_EXISTS,
95 1008 => DB_ERROR_CANNOT_DROP,
96 1022 => DB_ERROR_ALREADY_EXISTS,
97 1044 => DB_ERROR_ACCESS_VIOLATION,
98 1046 => DB_ERROR_NODBSELECTED,
99 1048 => DB_ERROR_CONSTRAINT,
100 1049 => DB_ERROR_NOSUCHDB,
101 1050 => DB_ERROR_ALREADY_EXISTS,
102 1051 => DB_ERROR_NOSUCHTABLE,
103 1054 => DB_ERROR_NOSUCHFIELD,
104 1061 => DB_ERROR_ALREADY_EXISTS,
105 1062 => DB_ERROR_ALREADY_EXISTS,
106 1064 => DB_ERROR_SYNTAX,
107 1091 => DB_ERROR_NOT_FOUND,
108 1100 => DB_ERROR_NOT_LOCKED,
109 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
110 1142 => DB_ERROR_ACCESS_VIOLATION,
111 1146 => DB_ERROR_NOSUCHTABLE,
112 1216 => DB_ERROR_CONSTRAINT,
113 1217 => DB_ERROR_CONSTRAINT,
114 1356 => DB_ERROR_INVALID_VIEW,
115 1365 => DB_ERROR_DIVZERO,
116 1451 => DB_ERROR_CONSTRAINT,
117 1452 => DB_ERROR_CONSTRAINT,
118 );
119
120 /**
121 * The raw database connection created by PHP
122 * @var resource
123 */
124 var $connection;
125
126 /**
127 * The DSN information for connecting to a database
128 * @var array
129 */
130 var $dsn = array();
131
132
133 /**
134 * Should data manipulation queries be committed automatically?
135 * @var bool
136 * @access private
137 */
138 var $autocommit = true;
139
140 /**
141 * The quantity of transactions begun
142 *
143 * {@internal While this is private, it can't actually be designated
144 * private in PHP 5 because it is directly accessed in the test suite.}}
145 *
146 * @var integer
147 * @access private
148 */
149 var $transaction_opcount = 0;
150
151 /**
152 * The database specified in the DSN
153 *
154 * It's a fix to allow calls to different databases in the same script.
155 *
156 * @var string
157 * @access private
158 */
159 var $_db = '';
160
161
162 // }}}
163 // {{{ constructor
164
165 /**
166 * This constructor calls <kbd>$this->DB_common()</kbd>
167 *
168 * @return void
169 */
170 function DB_mysql()
171 {
172 $this->DB_common();
173 }
174
175 // }}}
176 // {{{ connect()
177
178 /**
179 * Connect to the database server, log in and open the database
180 *
181 * Don't call this method directly. Use DB::connect() instead.
182 *
183 * PEAR DB's mysql driver supports the following extra DSN options:
184 * + new_link If set to true, causes subsequent calls to connect()
185 * to return a new connection link instead of the
186 * existing one. WARNING: this is not portable to
187 * other DBMS's. Available since PEAR DB 1.7.0.
188 * + client_flags Any combination of MYSQL_CLIENT_* constants.
189 * Only used if PHP is at version 4.3.0 or greater.
190 * Available since PEAR DB 1.7.0.
191 *
192 * @param array $dsn the data source name
193 * @param bool $persistent should the connection be persistent?
194 *
195 * @return int DB_OK on success. A DB_Error object on failure.
196 */
197 function connect($dsn, $persistent = false)
198 {
199 if (!PEAR::loadExtension('mysql')) {
200 return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
201 }
202
203 $this->dsn = $dsn;
204 if ($dsn['dbsyntax']) {
205 $this->dbsyntax = $dsn['dbsyntax'];
206 }
207
208 $params = array();
209 if ($dsn['protocol'] && $dsn['protocol'] == 'unix') {
210 $params[0] = ':' . $dsn['socket'];
211 } else {
212 $params[0] = $dsn['hostspec'] ? $dsn['hostspec']
213 : 'localhost';
214 if ($dsn['port']) {
215 $params[0] .= ':' . $dsn['port'];
216 }
217 }
218 $params[] = $dsn['username'] ? $dsn['username'] : null;
219 $params[] = $dsn['password'] ? $dsn['password'] : null;
220
221 if (!$persistent) {
222 if (isset($dsn['new_link'])
223 && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
224 {
225 $params[] = true;
226 } else {
227 $params[] = false;
228 }
229 }
230 if (version_compare(phpversion(), '4.3.0', '>=')) {
231 $params[] = isset($dsn['client_flags'])
232 ? $dsn['client_flags'] : null;
233 }
234
235 $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
236
237 $ini = ini_get('track_errors');
238 $php_errormsg = '';
239 if ($ini) {
240 $this->connection = @call_user_func_array($connect_function,
241 $params);
242 } else {
243 @ini_set('track_errors', 1);
244 $this->connection = @call_user_func_array($connect_function,
245 $params);
246 @ini_set('track_errors', $ini);
247 }
248
249 if (!$this->connection) {
250 if (($err = @mysql_error()) != '') {
251 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
252 null, null, null,
253 $err);
254 } else {
255 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
256 null, null, null,
257 $php_errormsg);
258 }
259 }
260
261 if ($dsn['database']) {
262 if (!@mysql_select_db($dsn['database'], $this->connection)) {
263 return $this->mysqlRaiseError();
264 }
265 $this->_db = $dsn['database'];
266 }
267
268 return DB_OK;
269 }
270
271 // }}}
272 // {{{ disconnect()
273
274 /**
275 * Disconnects from the database server
276 *
277 * @return bool TRUE on success, FALSE on failure
278 */
279 function disconnect()
280 {
281 $ret = @mysql_close($this->connection);
282 $this->connection = null;
283 return $ret;
284 }
285
286 // }}}
287 // {{{ simpleQuery()
288
289 /**
290 * Sends a query to the database server
291 *
292 * Generally uses mysql_query(). If you want to use
293 * mysql_unbuffered_query() set the "result_buffering" option to 0 using
294 * setOptions(). This option was added in Release 1.7.0.
295 *
296 * @param string the SQL query string
297 *
298 * @return mixed + a PHP result resrouce for successful SELECT queries
299 * + the DB_OK constant for other successful queries
300 * + a DB_Error object on failure
301 */
302 function simpleQuery($query)
303 {
304 $ismanip = $this->_checkManip($query);
305 $this->last_query = $query;
306 $query = $this->modifyQuery($query);
307 if ($this->_db) {
308 if (!@mysql_select_db($this->_db, $this->connection)) {
309 return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
310 }
311 }
312 if (!$this->autocommit && $ismanip) {
313 if ($this->transaction_opcount == 0) {
314 $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
315 $result = @mysql_query('BEGIN', $this->connection);
316 if (!$result) {
317 return $this->mysqlRaiseError();
318 }
319 }
320 $this->transaction_opcount++;
321 }
322 if (!$this->options['result_buffering']) {
323 $result = @mysql_unbuffered_query($query, $this->connection);
324 } else {
325 $result = @mysql_query($query, $this->connection);
326 }
327 if (!$result) {
328 return $this->mysqlRaiseError();
329 }
330 if (is_resource($result)) {
331 return $result;
332 }
333 return DB_OK;
334 }
335
336 // }}}
337 // {{{ nextResult()
338
339 /**
340 * Move the internal mysql result pointer to the next available result
341 *
342 * This method has not been implemented yet.
343 *
344 * @param a valid sql result resource
345 *
346 * @return false
347 */
348 function nextResult($result)
349 {
350 return false;
351 }
352
353 // }}}
354 // {{{ fetchInto()
355
356 /**
357 * Places a row from the result set into the given array
358 *
359 * Formating of the array and the data therein are configurable.
360 * See DB_result::fetchInto() for more information.
361 *
362 * This method is not meant to be called directly. Use
363 * DB_result::fetchInto() instead. It can't be declared "protected"
364 * because DB_result is a separate object.
365 *
366 * @param resource $result the query result resource
367 * @param array $arr the referenced array to put the data in
368 * @param int $fetchmode how the resulting array should be indexed
369 * @param int $rownum the row number to fetch (0 = first row)
370 *
371 * @return mixed DB_OK on success, NULL when the end of a result set is
372 * reached or on failure
373 *
374 * @see DB_result::fetchInto()
375 */
376 function fetchInto($result, &$arr, $fetchmode, $rownum = null)
377 {
378 if ($rownum !== null) {
379 if (!@mysql_data_seek($result, $rownum)) {
380 return null;
381 }
382 }
383 if ($fetchmode & DB_FETCHMODE_ASSOC) {
384 $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
385 if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
386 $arr = array_change_key_case($arr, CASE_LOWER);
387 }
388 } else {
389 $arr = @mysql_fetch_row($result);
390 }
391 if (!$arr) {
392 return null;
393 }
394 if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
395 /*
396 * Even though this DBMS already trims output, we do this because
397 * a field might have intentional whitespace at the end that
398 * gets removed by DB_PORTABILITY_RTRIM under another driver.
399 */
400 $this->_rtrimArrayValues($arr);
401 }
402 if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
403 $this->_convertNullArrayValuesToEmpty($arr);
404 }
405 return DB_OK;
406 }
407
408 // }}}
409 // {{{ freeResult()
410
411 /**
412 * Deletes the result set and frees the memory occupied by the result set
413 *
414 * This method is not meant to be called directly. Use
415 * DB_result::free() instead. It can't be declared "protected"
416 * because DB_result is a separate object.
417 *
418 * @param resource $result PHP's query result resource
419 *
420 * @return bool TRUE on success, FALSE if $result is invalid
421 *
422 * @see DB_result::free()
423 */
424 function freeResult($result)
425 {
426 return is_resource($result) ? mysql_free_result($result) : false;
427 }
428
429 // }}}
430 // {{{ numCols()
431
432 /**
433 * Gets the number of columns in a result set
434 *
435 * This method is not meant to be called directly. Use
436 * DB_result::numCols() instead. It can't be declared "protected"
437 * because DB_result is a separate object.
438 *
439 * @param resource $result PHP's query result resource
440 *
441 * @return int the number of columns. A DB_Error object on failure.
442 *
443 * @see DB_result::numCols()
444 */
445 function numCols($result)
446 {
447 $cols = @mysql_num_fields($result);
448 if (!$cols) {
449 return $this->mysqlRaiseError();
450 }
451 return $cols;
452 }
453
454 // }}}
455 // {{{ numRows()
456
457 /**
458 * Gets the number of rows in a result set
459 *
460 * This method is not meant to be called directly. Use
461 * DB_result::numRows() instead. It can't be declared "protected"
462 * because DB_result is a separate object.
463 *
464 * @param resource $result PHP's query result resource
465 *
466 * @return int the number of rows. A DB_Error object on failure.
467 *
468 * @see DB_result::numRows()
469 */
470 function numRows($result)
471 {
472 $rows = @mysql_num_rows($result);
473 if ($rows === null) {
474 return $this->mysqlRaiseError();
475 }
476 return $rows;
477 }
478
479 // }}}
480 // {{{ autoCommit()
481
482 /**
483 * Enables or disables automatic commits
484 *
485 * @param bool $onoff true turns it on, false turns it off
486 *
487 * @return int DB_OK on success. A DB_Error object if the driver
488 * doesn't support auto-committing transactions.
489 */
490 function autoCommit($onoff = false)
491 {
492 // XXX if $this->transaction_opcount > 0, we should probably
493 // issue a warning here.
494 $this->autocommit = $onoff ? true : false;
495 return DB_OK;
496 }
497
498 // }}}
499 // {{{ commit()
500
501 /**
502 * Commits the current transaction
503 *
504 * @return int DB_OK on success. A DB_Error object on failure.
505 */
506 function commit()
507 {
508 if ($this->transaction_opcount > 0) {
509 if ($this->_db) {
510 if (!@mysql_select_db($this->_db, $this->connection)) {
511 return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
512 }
513 }
514 $result = @mysql_query('COMMIT', $this->connection);
515 $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
516 $this->transaction_opcount = 0;
517 if (!$result) {
518 return $this->mysqlRaiseError();
519 }
520 }
521 return DB_OK;
522 }
523
524 // }}}
525 // {{{ rollback()
526
527 /**
528 * Reverts the current transaction
529 *
530 * @return int DB_OK on success. A DB_Error object on failure.
531 */
532 function rollback()
533 {
534 if ($this->transaction_opcount > 0) {
535 if ($this->_db) {
536 if (!@mysql_select_db($this->_db, $this->connection)) {
537 return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
538 }
539 }
540 $result = @mysql_query('ROLLBACK', $this->connection);
541 $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
542 $this->transaction_opcount = 0;
543 if (!$result) {
544 return $this->mysqlRaiseError();
545 }
546 }
547 return DB_OK;
548 }
549
550 // }}}
551 // {{{ affectedRows()
552
553 /**
554 * Determines the number of rows affected by a data maniuplation query
555 *
556 * 0 is returned for queries that don't manipulate data.
557 *
558 * @return int the number of rows. A DB_Error object on failure.
559 */
560 function affectedRows()
561 {
562 if ($this->_last_query_manip) {
563 return @mysql_affected_rows($this->connection);
564 } else {
565 return 0;
566 }
567 }
568
569 // }}}
570 // {{{ nextId()
571
572 /**
573 * Returns the next free id in a sequence
574 *
575 * @param string $seq_name name of the sequence
576 * @param boolean $ondemand when true, the seqence is automatically
577 * created if it does not exist
578 *
579 * @return int the next id number in the sequence.
580 * A DB_Error object on failure.
581 *
582 * @see DB_common::nextID(), DB_common::getSequenceName(),
583 * DB_mysql::createSequence(), DB_mysql::dropSequence()
584 */
585 function nextId($seq_name, $ondemand = true)
586 {
587 $seqname = $this->getSequenceName($seq_name);
588 do {
589 $repeat = 0;
590 $this->pushErrorHandling(PEAR_ERROR_RETURN);
591 $result = $this->query("UPDATE ${seqname} ".
592 'SET id=LAST_INSERT_ID(id+1)');
593 $this->popErrorHandling();
594 if ($result === DB_OK) {
595 // COMMON CASE
596 $id = @mysql_insert_id($this->connection);
597 if ($id != 0) {
598 return $id;
599 }
600 // EMPTY SEQ TABLE
601 // Sequence table must be empty for some reason, so fill
602 // it and return 1 and obtain a user-level lock
603 $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
604 if (DB::isError($result)) {
605 return $this->raiseError($result);
606 }
607 if ($result == 0) {
608 // Failed to get the lock
609 return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
610 }
611
612 // add the default value
613 $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
614 if (DB::isError($result)) {
615 return $this->raiseError($result);
616 }
617
618 // Release the lock
619 $result = $this->getOne('SELECT RELEASE_LOCK('
620 . "'${seqname}_lock')");
621 if (DB::isError($result)) {
622 return $this->raiseError($result);
623 }
624 // We know what the result will be, so no need to try again
625 return 1;
626
627 } elseif ($ondemand && DB::isError($result) &&
628 $result->getCode() == DB_ERROR_NOSUCHTABLE)
629 {
630 // ONDEMAND TABLE CREATION
631 $result = $this->createSequence($seq_name);
632 if (DB::isError($result)) {
633 return $this->raiseError($result);
634 } else {
635 $repeat = 1;
636 }
637
638 } elseif (DB::isError($result) &&
639 $result->getCode() == DB_ERROR_ALREADY_EXISTS)
640 {
641 // BACKWARDS COMPAT
642 // see _BCsequence() comment
643 $result = $this->_BCsequence($seqname);
644 if (DB::isError($result)) {
645 return $this->raiseError($result);
646 }
647 $repeat = 1;
648 }
649 } while ($repeat);
650
651 return $this->raiseError($result);
652 }
653
654 // }}}
655 // {{{ createSequence()
656
657 /**
658 * Creates a new sequence
659 *
660 * @param string $seq_name name of the new sequence
661 *
662 * @return int DB_OK on success. A DB_Error object on failure.
663 *
664 * @see DB_common::createSequence(), DB_common::getSequenceName(),
665 * DB_mysql::nextID(), DB_mysql::dropSequence()
666 */
667 function createSequence($seq_name)
668 {
669 $seqname = $this->getSequenceName($seq_name);
670 $res = $this->query('CREATE TABLE ' . $seqname
671 . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
672 . ' PRIMARY KEY(id))');
673 if (DB::isError($res)) {
674 return $res;
675 }
676 // insert yields value 1, nextId call will generate ID 2
677 $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
678 if (DB::isError($res)) {
679 return $res;
680 }
681 // so reset to zero
682 return $this->query("UPDATE ${seqname} SET id = 0");
683 }
684
685 // }}}
686 // {{{ dropSequence()
687
688 /**
689 * Deletes a sequence
690 *
691 * @param string $seq_name name of the sequence to be deleted
692 *
693 * @return int DB_OK on success. A DB_Error object on failure.
694 *
695 * @see DB_common::dropSequence(), DB_common::getSequenceName(),
696 * DB_mysql::nextID(), DB_mysql::createSequence()
697 */
698 function dropSequence($seq_name)
699 {
700 return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
701 }
702
703 // }}}
704 // {{{ _BCsequence()
705
706 /**
707 * Backwards compatibility with old sequence emulation implementation
708 * (clean up the dupes)
709 *
710 * @param string $seqname the sequence name to clean up
711 *
712 * @return bool true on success. A DB_Error object on failure.
713 *
714 * @access private
715 */
716 function _BCsequence($seqname)
717 {
718 // Obtain a user-level lock... this will release any previous
719 // application locks, but unlike LOCK TABLES, it does not abort
720 // the current transaction and is much less frequently used.
721 $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
722 if (DB::isError($result)) {
723 return $result;
724 }
725 if ($result == 0) {
726 // Failed to get the lock, can't do the conversion, bail
727 // with a DB_ERROR_NOT_LOCKED error
728 return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
729 }
730
731 $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
732 if (DB::isError($highest_id)) {
733 return $highest_id;
734 }
735 // This should kill all rows except the highest
736 // We should probably do something if $highest_id isn't
737 // numeric, but I'm at a loss as how to handle that...
738 $result = $this->query('DELETE FROM ' . $seqname
739 . " WHERE id <> $highest_id");
740 if (DB::isError($result)) {
741 return $result;
742 }
743
744 // If another thread has been waiting for this lock,
745 // it will go thru the above procedure, but will have no
746 // real effect
747 $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
748 if (DB::isError($result)) {
749 return $result;
750 }
751 return true;
752 }
753
754 // }}}
755 // {{{ quoteIdentifier()
756
757 /**
758 * Quotes a string so it can be safely used as a table or column name
759 * (WARNING: using names that require this is a REALLY BAD IDEA)
760 *
761 * WARNING: Older versions of MySQL can't handle the backtick
762 * character (<kbd>`</kbd>) in table or column names.
763 *
764 * @param string $str identifier name to be quoted
765 *
766 * @return string quoted identifier string
767 *
768 * @see DB_common::quoteIdentifier()
769 * @since Method available since Release 1.6.0
770 */
771 function quoteIdentifier($str)
772 {
773 return '`' . str_replace('`', '``', $str) . '`';
774 }
775
776 // }}}
777 // {{{ quote()
778
779 /**
780 * @deprecated Deprecated in release 1.6.0
781 */
782 function quote($str = null)
783 {
784 return $this->quoteSmart($str);
785 }
786
787 // }}}
788 // {{{ escapeSimple()
789
790 /**
791 * Escapes a string according to the current DBMS's standards
792 *
793 * @param string $str the string to be escaped
794 *
795 * @return string the escaped string
796 *
797 * @see DB_common::quoteSmart()
798 * @since Method available since Release 1.6.0
799 */
800 function escapeSimple($str)
801 {
802 return @mysql_real_escape_string($str, $this->connection);
803 }
804
805 // }}}
806 // {{{ modifyQuery()
807
808 /**
809 * Changes a query string for various DBMS specific reasons
810 *
811 * This little hack lets you know how many rows were deleted
812 * when running a "DELETE FROM table" query. Only implemented
813 * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
814 *
815 * @param string $query the query string to modify
816 *
817 * @return string the modified query string
818 *
819 * @access protected
820 * @see DB_common::setOption()
821 */
822 function modifyQuery($query)
823 {
824 if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
825 // "DELETE FROM table" gives 0 affected rows in MySQL.
826 // This little hack lets you know how many rows were deleted.
827 if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
828 $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
829 'DELETE FROM \1 WHERE 1=1', $query);
830 }
831 }
832 return $query;
833 }
834
835 // }}}
836 // {{{ modifyLimitQuery()
837
838 /**
839 * Adds LIMIT clauses to a query string according to current DBMS standards
840 *
841 * @param string $query the query to modify
842 * @param int $from the row to start to fetching (0 = the first row)
843 * @param int $count the numbers of rows to fetch
844 * @param mixed $params array, string or numeric data to be used in
845 * execution of the statement. Quantity of items
846 * passed must match quantity of placeholders in
847 * query: meaning 1 placeholder for non-array
848 * parameters or 1 placeholder per array element.
849 *
850 * @return string the query string with LIMIT clauses added
851 *
852 * @access protected
853 */
854 function modifyLimitQuery($query, $from, $count, $params = array())
855 {
856 if (DB::isManip($query) || $this->_next_query_manip) {
857 return $query . " LIMIT $count";
858 } else {
859 return $query . " LIMIT $from, $count";
860 }
861 }
862
863 // }}}
864 // {{{ mysqlRaiseError()
865
866 /**
867 * Produces a DB_Error object regarding the current problem
868 *
869 * @param int $errno if the error is being manually raised pass a
870 * DB_ERROR* constant here. If this isn't passed
871 * the error information gathered from the DBMS.
872 *
873 * @return object the DB_Error object
874 *
875 * @see DB_common::raiseError(),
876 * DB_mysql::errorNative(), DB_common::errorCode()
877 */
878 function mysqlRaiseError($errno = null)
879 {
880 if ($errno === null) {
881 if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
882 $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
883 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
884 $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
885 } else {
886 // Doing this in case mode changes during runtime.
887 $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
888 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
889 $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
890 }
891 $errno = $this->errorCode(mysql_errno($this->connection));
892 }
893 return $this->raiseError($errno, null, null, null,
894 @mysql_errno($this->connection) . ' ** ' .
895 @mysql_error($this->connection));
896 }
897
898 // }}}
899 // {{{ errorNative()
900
901 /**
902 * Gets the DBMS' native error code produced by the last query
903 *
904 * @return int the DBMS' error code
905 */
906 function errorNative()
907 {
908 return @mysql_errno($this->connection);
909 }
910
911 // }}}
912 // {{{ tableInfo()
913
914 /**
915 * Returns information about a table or a result set
916 *
917 * @param object|string $result DB_result object from a query or a
918 * string containing the name of a table.
919 * While this also accepts a query result
920 * resource identifier, this behavior is
921 * deprecated.
922 * @param int $mode a valid tableInfo mode
923 *
924 * @return array an associative array with the information requested.
925 * A DB_Error object on failure.
926 *
927 * @see DB_common::tableInfo()
928 */
929 function tableInfo($result, $mode = null)
930 {
931 if (is_string($result)) {
932 // Fix for bug #11580.
933 if ($this->_db) {
934 if (!@mysql_select_db($this->_db, $this->connection)) {
935 return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
936 }
937 }
938
939 /*
940 * Probably received a table name.
941 * Create a result resource identifier.
942 */
943 $id = @mysql_query("SELECT * FROM $result LIMIT 0",
944 $this->connection);
945 $got_string = true;
946 } elseif (isset($result->result)) {
947 /*
948 * Probably received a result object.
949 * Extract the result resource identifier.
950 */
951 $id = $result->result;
952 $got_string = false;
953 } else {
954 /*
955 * Probably received a result resource identifier.
956 * Copy it.
957 * Deprecated. Here for compatibility only.
958 */
959 $id = $result;
960 $got_string = false;
961 }
962
963 if (!is_resource($id)) {
964 return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
965 }
966
967 if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
968 $case_func = 'strtolower';
969 } else {
970 $case_func = 'strval';
971 }
972
973 $count = @mysql_num_fields($id);
974 $res = array();
975
976 if ($mode) {
977 $res['num_fields'] = $count;
978 }
979
980 for ($i = 0; $i < $count; $i++) {
981 $res[$i] = array(
982 'table' => $case_func(@mysql_field_table($id, $i)),
983 'name' => $case_func(@mysql_field_name($id, $i)),
984 'type' => @mysql_field_type($id, $i),
985 'len' => @mysql_field_len($id, $i),
986 'flags' => @mysql_field_flags($id, $i),
987 );
988 if ($mode & DB_TABLEINFO_ORDER) {
989 $res['order'][$res[$i]['name']] = $i;
990 }
991 if ($mode & DB_TABLEINFO_ORDERTABLE) {
992 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
993 }
994 }
995
996 // free the result only if we were called on a table
997 if ($got_string) {
998 @mysql_free_result($id);
999 }
1000 return $res;
1001 }
1002
1003 // }}}
1004 // {{{ getSpecialQuery()
1005
1006 /**
1007 * Obtains the query string needed for listing a given type of objects
1008 *
1009 * @param string $type the kind of objects you want to retrieve
1010 *
1011 * @return string the SQL query string or null if the driver doesn't
1012 * support the object type requested
1013 *
1014 * @access protected
1015 * @see DB_common::getListOf()
1016 */
1017 function getSpecialQuery($type)
1018 {
1019 switch ($type) {
1020 case 'tables':
1021 return 'SHOW TABLES';
1022 case 'users':
1023 return 'SELECT DISTINCT User FROM mysql.user';
1024 case 'databases':
1025 return 'SHOW DATABASES';
1026 default:
1027 return null;
1028 }
1029 }
1030
1031 // }}}
1032
1033 }
1034
1035 /*
1036 * Local variables:
1037 * tab-width: 4
1038 * c-basic-offset: 4
1039 * End:
1040 */
1041
1042 ?>