3 V4.98 13 Feb 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
4 Released under both BSD license and Lesser GPL library license.
5 Whenever there is any discrepancy between the two licenses,
6 the BSD license will take precedence.
9 MySQL code that does not support transactions. Use mysqlt if you need transactions.
10 Requires mysql client. Works on Windows and Unix.
12 28 Feb 2001: MetaColumns bug fix - suggested by Freek Dijkstra (phpeverywhere@macfreek.com)
15 // security - hide paths
16 if (!defined('ADODB_DIR')) die();
18 if (! defined("_ADODB_MYSQL_LAYER")) {
19 define("_ADODB_MYSQL_LAYER", 1 );
21 class ADODB_mysql
extends ADOConnection
{
22 var $databaseType = 'mysql';
23 var $dataProvider = 'mysql';
24 var $hasInsertID = true;
25 var $hasAffectedRows = true;
26 var $metaTablesSQL = "SHOW TABLES";
27 var $metaColumnsSQL = "SHOW COLUMNS FROM `%s`";
28 var $fmtTimeStamp = "'Y-m-d H:i:s'";
30 var $hasMoveFirst = true;
32 var $isoDates = true; // accepts dates in ISO format
33 var $sysDate = 'CURDATE()';
34 var $sysTimeStamp = 'NOW()';
35 var $hasTransactions = false;
36 var $forceNewConnect = false;
37 var $poorAffectedRows = true;
39 var $substr = "substring";
40 var $nameQuote = '`'; /// string to use to quote identifiers and names
41 var $compat323 = false; // true if compat with mysql 3.23
43 function ADODB_mysql()
45 if (defined('ADODB_EXTENSION')) $this->rsPrefix
.= 'ext_';
50 $arr['description'] = ADOConnection
::GetOne("select version()");
51 $arr['version'] = ADOConnection
::_findvers($arr['description']);
55 function IfNull( $field, $ifNull )
57 return " IFNULL($field, $ifNull) "; // if MySQL
61 function &MetaTables($ttype=false,$showSchema=false,$mask=false)
63 $save = $this->metaTablesSQL
;
64 if ($showSchema && is_string($showSchema)) {
65 $this->metaTablesSQL
.= " from $showSchema";
69 $mask = $this->qstr($mask);
70 $this->metaTablesSQL
.= " like $mask";
72 $ret =& ADOConnection
::MetaTables($ttype,$showSchema);
74 $this->metaTablesSQL
= $save;
79 function &MetaIndexes ($table, $primary = FALSE, $owner=false)
81 // save old fetch mode
82 global $ADODB_FETCH_MODE;
85 $save = $ADODB_FETCH_MODE;
86 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
87 if ($this->fetchMode
!== FALSE) {
88 $savem = $this->SetFetchMode(FALSE);
92 $rs = $this->Execute(sprintf('SHOW INDEX FROM %s',$table));
96 $this->SetFetchMode($savem);
98 $ADODB_FETCH_MODE = $save;
100 if (!is_object($rs)) {
106 // parse index data into array
107 while ($row = $rs->FetchRow()) {
108 if ($primary == FALSE AND $row[2] == 'PRIMARY') {
112 if (!isset($indexes[$row[2]])) {
113 $indexes[$row[2]] = array(
114 'unique' => ($row[1] == 0),
119 $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
122 // sort columns by order in the index
123 foreach ( array_keys ($indexes) as $index )
125 ksort ($indexes[$index]['columns']);
132 // if magic quotes disabled, use mysql_real_escape_string()
133 function qstr($s,$magic_quotes=false)
135 if (is_null($s)) return 'NULL';
137 if (!$magic_quotes) {
139 if (ADODB_PHPVER
>= 0x4300) {
140 if (is_resource($this->_connectionID
))
141 return "'".mysql_real_escape_string($s,$this->_connectionID
)."'";
143 if ($this->replaceQuote
[0] == '\\'){
144 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
146 return "'".str_replace("'",$this->replaceQuote
,$s)."'";
149 // undo magic quotes for "
150 $s = str_replace('\\"','"',$s);
156 return ADOConnection
::GetOne('SELECT LAST_INSERT_ID()');
157 //return mysql_insert_id($this->_connectionID);
160 function GetOne($sql,$inputarr=false)
162 if ($this->compat323
== false && strncasecmp($sql,'sele',4) == 0) {
163 $rs =& $this->SelectLimit($sql,1,-1,$inputarr);
166 if ($rs->EOF
) return false;
167 return reset($rs->fields
);
170 return ADOConnection
::GetOne($sql,$inputarr);
175 function BeginTrans()
177 if ($this->debug
) ADOConnection
::outp("Transactions not supported in 'mysql' driver. Use 'mysqlt' or 'mysqli' driver");
180 function _affectedrows()
182 return mysql_affected_rows($this->_connectionID
);
185 // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html
186 // Reference on Last_Insert_ID on the recommended way to simulate sequences
187 var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
188 var $_genSeqSQL = "create table %s (id int not null)";
189 var $_genSeqCountSQL = "select count(*) from %s";
190 var $_genSeq2SQL = "insert into %s values (%s)";
191 var $_dropSeqSQL = "drop table %s";
193 function CreateSequence($seqname='adodbseq',$startID=1)
195 if (empty($this->_genSeqSQL
)) return false;
196 $u = strtoupper($seqname);
198 $ok = $this->Execute(sprintf($this->_genSeqSQL
,$seqname));
199 if (!$ok) return false;
200 return $this->Execute(sprintf($this->_genSeq2SQL
,$seqname,$startID-1));
204 function GenID($seqname='adodbseq',$startID=1)
206 // post-nuke sets hasGenID to false
207 if (!$this->hasGenID
) return false;
209 $savelog = $this->_logsql
;
210 $this->_logsql
= false;
211 $getnext = sprintf($this->_genIDSQL
,$seqname);
212 $holdtransOK = $this->_transOK
; // save the current status
213 $rs = @$this->Execute($getnext);
215 if ($holdtransOK) $this->_transOK
= true; //if the status was ok before reset
216 $u = strtoupper($seqname);
217 $this->Execute(sprintf($this->_genSeqSQL
,$seqname));
218 $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL
,$seqname));
219 if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL
,$seqname,$startID-1));
220 $rs = $this->Execute($getnext);
224 $this->genID
= mysql_insert_id($this->_connectionID
);
229 $this->_logsql
= $savelog;
233 function &MetaDatabases()
235 $qid = mysql_list_dbs($this->_connectionID
);
238 $max = mysql_num_rows($qid);
240 $db = mysql_tablename($qid,$i);
241 if ($db != 'mysql') $arr[] = $db;
248 // Format date column in sql string given an input format that understands Y M D
249 function SQLDate($fmt, $col=false)
251 if (!$col) $col = $this->sysTimeStamp
;
252 $s = 'DATE_FORMAT('.$col.",'";
255 for ($i=0; $i < $len; $i++
) {
262 $ch = substr($fmt,$i,1);
288 $s .= "'),Quarter($col)";
290 if ($len > $i+
1) $s .= ",DATE_FORMAT($col,'";
330 if ($concat) $s = "CONCAT($s)";
335 // returns concatenated string
336 // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
340 $arr = func_get_args();
342 // suggestion by andrew005@mnogo.ru
343 $s = implode(',',$arr);
344 if (strlen($s) > 0) return "CONCAT($s)";
348 function OffsetDate($dayFraction,$date=false)
350 if (!$date) $date = $this->sysDate
;
352 $fraction = $dayFraction * 24 * 3600;
353 return $date . ' + INTERVAL ' . $fraction.' SECOND';
355 // return "from_unixtime(unix_timestamp($date)+$fraction)";
358 // returns true or false
359 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
361 if (!empty($this->port
)) $argHostname .= ":".$this->port
;
363 if (ADODB_PHPVER
>= 0x4300)
364 $this->_connectionID
= mysql_connect($argHostname,$argUsername,$argPassword,
365 $this->forceNewConnect
,$this->clientFlags
);
366 else if (ADODB_PHPVER
>= 0x4200)
367 $this->_connectionID
= mysql_connect($argHostname,$argUsername,$argPassword,
368 $this->forceNewConnect
);
370 $this->_connectionID
= mysql_connect($argHostname,$argUsername,$argPassword);
372 if ($this->_connectionID
=== false) return false;
373 if ($argDatabasename) return $this->SelectDB($argDatabasename);
377 // returns true or false
378 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
380 if (!empty($this->port
)) $argHostname .= ":".$this->port
;
382 if (ADODB_PHPVER
>= 0x4300)
383 $this->_connectionID
= mysql_pconnect($argHostname,$argUsername,$argPassword,$this->clientFlags
);
385 $this->_connectionID
= mysql_pconnect($argHostname,$argUsername,$argPassword);
386 if ($this->_connectionID
=== false) return false;
387 if ($this->autoRollback
) $this->RollbackTrans();
388 if ($argDatabasename) return $this->SelectDB($argDatabasename);
392 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
394 $this->forceNewConnect
= true;
395 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
398 function &MetaColumns($table)
400 $this->_findschema($table,$schema);
402 $dbName = $this->database
;
403 $this->SelectDB($schema);
405 global $ADODB_FETCH_MODE;
406 $save = $ADODB_FETCH_MODE;
407 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
409 if ($this->fetchMode
!== false) $savem = $this->SetFetchMode(false);
410 $rs = $this->Execute(sprintf($this->metaColumnsSQL
,$table));
413 $this->SelectDB($dbName);
416 if (isset($savem)) $this->SetFetchMode($savem);
417 $ADODB_FETCH_MODE = $save;
418 if (!is_object($rs)) {
425 $fld = new ADOFieldObject();
426 $fld->name
= $rs->fields
[0];
427 $type = $rs->fields
[1];
429 // split type into type(length):
431 if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
432 $fld->type
= $query_array[1];
433 $fld->max_length
= is_numeric($query_array[2]) ?
$query_array[2] : -1;
434 $fld->scale
= is_numeric($query_array[3]) ?
$query_array[3] : -1;
435 } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
436 $fld->type
= $query_array[1];
437 $fld->max_length
= is_numeric($query_array[2]) ?
$query_array[2] : -1;
438 } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
439 $fld->type
= $query_array[1];
440 $arr = explode(",",$query_array[2]);
442 $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
443 $fld->max_length
= ($zlen > 0) ?
$zlen : 1;
446 $fld->max_length
= -1;
448 $fld->not_null
= ($rs->fields
[2] != 'YES');
449 $fld->primary_key
= ($rs->fields
[3] == 'PRI');
450 $fld->auto_increment
= (strpos($rs->fields
[5], 'auto_increment') !== false);
451 $fld->binary
= (strpos($type,'blob') !== false);
452 $fld->unsigned
= (strpos($type,'unsigned') !== false);
453 $fld->zerofill
= (strpos($type,'zerofill') !== false);
457 if ($d != '' && $d != 'NULL') {
458 $fld->has_default
= true;
459 $fld->default_value
= $d;
461 $fld->has_default
= false;
465 if ($save == ADODB_FETCH_NUM
) {
468 $retarr[strtoupper($fld->name
)] = $fld;
477 // returns true or false
478 function SelectDB($dbName)
480 $this->database
= $dbName;
481 $this->databaseName
= $dbName; # obsolete, retained for compat with older adodb versions
482 if ($this->_connectionID
) {
483 return @mysql_select_db
($dbName,$this->_connectionID
);
488 // parameters use PostgreSQL convention, not MySQL
489 function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0)
491 $offsetStr =($offset>=0) ?
((integer)$offset)."," : '';
492 // jason judge, see http://phplens.com/lens/lensforum/msgs.php?id=9220
493 if ($nrows < 0) $nrows = '18446744073709551615';
496 $rs =& $this->CacheExecute($secs,$sql." LIMIT $offsetStr".((integer)$nrows),$inputarr);
498 $rs =& $this->Execute($sql." LIMIT $offsetStr".((integer)$nrows),$inputarr);
502 // returns queryID or false
503 function _query($sql,$inputarr)
505 //global $ADODB_COUNTRECS;
506 //if($ADODB_COUNTRECS)
507 return mysql_query($sql,$this->_connectionID
);
508 //else return @mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6
511 /* Returns: the last error message from previous database operation */
515 if ($this->_logsql
) return $this->_errorMsg
;
516 if (empty($this->_connectionID
)) $this->_errorMsg
= @mysql_error
();
517 else $this->_errorMsg
= @mysql_error
($this->_connectionID
);
518 return $this->_errorMsg
;
521 /* Returns: the last error number from previous database operation */
524 if ($this->_logsql
) return $this->_errorCode
;
525 if (empty($this->_connectionID
)) return @mysql_errno
();
526 else return @mysql_errno
($this->_connectionID
);
529 // returns true or false
532 @mysql_close
($this->_connectionID
);
533 $this->_connectionID
= false;
538 * Maximum size of C field
546 * Maximum size of X field
553 // "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx>
554 function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE )
556 global $ADODB_FETCH_MODE;
557 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC ||
$this->fetchMode
== ADODB_FETCH_ASSOC
) $associative = true;
559 if ( !empty($owner) ) {
560 $table = "$owner.$table";
562 $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
563 if ($associative) $create_sql = $a_create_table["Create Table"];
564 else $create_sql = $a_create_table[1];
568 if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
569 $foreign_keys = array();
570 $num_keys = count($matches[0]);
571 for ( $i = 0; $i < $num_keys; $i ++
) {
572 $my_field = explode('`, `', $matches[1][$i]);
573 $ref_table = $matches[2][$i];
574 $ref_field = explode('`, `', $matches[3][$i]);
577 $ref_table = strtoupper($ref_table);
580 $foreign_keys[$ref_table] = array();
581 $num_fields = count($my_field);
582 for ( $j = 0; $j < $num_fields; $j ++
) {
583 if ( $associative ) {
584 $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
586 $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
591 return $foreign_keys;
597 /*--------------------------------------------------------------------------------------
598 Class Name: Recordset
599 --------------------------------------------------------------------------------------*/
602 class ADORecordSet_mysql
extends ADORecordSet
{
604 var $databaseType = "mysql";
607 function ADORecordSet_mysql($queryID,$mode=false)
609 if ($mode === false) {
610 global $ADODB_FETCH_MODE;
611 $mode = $ADODB_FETCH_MODE;
615 case ADODB_FETCH_NUM
: $this->fetchMode
= MYSQL_NUM
; break;
616 case ADODB_FETCH_ASSOC
:$this->fetchMode
= MYSQL_ASSOC
; break;
617 case ADODB_FETCH_DEFAULT
:
618 case ADODB_FETCH_BOTH
:
620 $this->fetchMode
= MYSQL_BOTH
; break;
622 $this->adodbFetchMode
= $mode;
623 $this->ADORecordSet($queryID);
628 //GLOBAL $ADODB_COUNTRECS;
629 // $this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1;
630 $this->_numOfRows
= @mysql_num_rows
($this->_queryID
);
631 $this->_numOfFields
= @mysql_num_fields
($this->_queryID
);
634 function &FetchField($fieldOffset = -1)
636 if ($fieldOffset != -1) {
637 $o = @mysql_fetch_field
($this->_queryID
, $fieldOffset);
638 $f = @mysql_field_flags
($this->_queryID
,$fieldOffset);
639 $o->max_length
= @mysql_field_len
($this->_queryID
,$fieldOffset); // suggested by: Jim Nicholson (jnich#att.com)
640 //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
641 $o->binary
= (strpos($f,'binary')!== false);
643 else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */
644 $o = @mysql_fetch_field
($this->_queryID
);
645 $o->max_length
= @mysql_field_len
($this->_queryID
); // suggested by: Jim Nicholson (jnich#att.com)
646 //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
652 function &GetRowAssoc($upper=true)
654 if ($this->fetchMode
== MYSQL_ASSOC
&& !$upper) $row = $this->fields
;
655 else $row =& ADORecordSet
::GetRowAssoc($upper);
659 /* Use associative array to get fields array */
660 function Fields($colname)
662 // added @ by "Michael William Miller" <mille562@pilot.msu.edu>
663 if ($this->fetchMode
!= MYSQL_NUM
) return @$this->fields
[$colname];
666 $this->bind
= array();
667 for ($i=0; $i < $this->_numOfFields
; $i++
) {
668 $o = $this->FetchField($i);
669 $this->bind
[strtoupper($o->name
)] = $i;
672 return $this->fields
[$this->bind
[strtoupper($colname)]];
677 if ($this->_numOfRows
== 0) return false;
678 return @mysql_data_seek
($this->_queryID
,$row);
683 //return adodb_movenext($this);
684 //if (defined('ADODB_EXTENSION')) return adodb_movenext($this);
685 if (@$this->fields
= mysql_fetch_array($this->_queryID
,$this->fetchMode
)) {
686 $this->_currentRow +
= 1;
690 $this->_currentRow +
= 1;
698 $this->fields
= @mysql_fetch_array
($this->_queryID
,$this->fetchMode
);
699 return is_array($this->fields
);
703 @mysql_free_result
($this->_queryID
);
704 $this->_queryID
= false;
707 function MetaType($t,$len=-1,$fieldobj=false)
711 $t = $fieldobj->type
;
712 $len = $fieldobj->max_length
;
715 $len = -1; // mysql max_length is not accurate
716 switch (strtoupper($t)) {
724 if ($len <= $this->blobSize
) return 'C';
731 // php_mysql extension always returns 'blob' even if 'text'
732 // so we have to check whether binary...
737 return !empty($fieldobj->binary
) ?
'B' : 'X';
740 case 'DATE': return 'D';
744 case 'TIMESTAMP': return 'T';
753 if (!empty($fieldobj->primary_key
)) return 'R';
762 class ADORecordSet_ext_mysql
extends ADORecordSet_mysql
{
763 function ADORecordSet_ext_mysql($queryID,$mode=false)
765 if ($mode === false) {
766 global $ADODB_FETCH_MODE;
767 $mode = $ADODB_FETCH_MODE;
771 case ADODB_FETCH_NUM
: $this->fetchMode
= MYSQL_NUM
; break;
772 case ADODB_FETCH_ASSOC
:$this->fetchMode
= MYSQL_ASSOC
; break;
773 case ADODB_FETCH_DEFAULT
:
774 case ADODB_FETCH_BOTH
:
776 $this->fetchMode
= MYSQL_BOTH
; break;
778 $this->adodbFetchMode
= $mode;
779 $this->ADORecordSet($queryID);
784 return @adodb_movenext
($this);