3 V4.94 23 Jan 2007 (c) 2000-2007 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 (!$magic_quotes) {
137 if (ADODB_PHPVER
>= 0x4300) {
138 if (is_resource($this->_connectionID
))
139 return "'".mysql_real_escape_string($s,$this->_connectionID
)."'";
141 if ($this->replaceQuote
[0] == '\\'){
142 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
144 return "'".str_replace("'",$this->replaceQuote
,$s)."'";
147 // undo magic quotes for "
148 $s = str_replace('\\"','"',$s);
154 return ADOConnection
::GetOne('SELECT LAST_INSERT_ID()');
155 //return mysql_insert_id($this->_connectionID);
158 function GetOne($sql,$inputarr=false)
160 if ($this->compat323
== false && strncasecmp($sql,'sele',4) == 0) {
161 $rs =& $this->SelectLimit($sql,1,-1,$inputarr);
164 if ($rs->EOF
) return false;
165 return reset($rs->fields
);
168 return ADOConnection
::GetOne($sql,$inputarr);
173 function BeginTrans()
175 if ($this->debug
) ADOConnection
::outp("Transactions not supported in 'mysql' driver. Use 'mysqlt' or 'mysqli' driver");
178 function _affectedrows()
180 return mysql_affected_rows($this->_connectionID
);
183 // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html
184 // Reference on Last_Insert_ID on the recommended way to simulate sequences
185 var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
186 var $_genSeqSQL = "create table %s (id int not null)";
187 var $_genSeqCountSQL = "select count(*) from %s";
188 var $_genSeq2SQL = "insert into %s values (%s)";
189 var $_dropSeqSQL = "drop table %s";
191 function CreateSequence($seqname='adodbseq',$startID=1)
193 if (empty($this->_genSeqSQL
)) return false;
194 $u = strtoupper($seqname);
196 $ok = $this->Execute(sprintf($this->_genSeqSQL
,$seqname));
197 if (!$ok) return false;
198 return $this->Execute(sprintf($this->_genSeq2SQL
,$seqname,$startID-1));
202 function GenID($seqname='adodbseq',$startID=1)
204 // post-nuke sets hasGenID to false
205 if (!$this->hasGenID
) return false;
207 $savelog = $this->_logsql
;
208 $this->_logsql
= false;
209 $getnext = sprintf($this->_genIDSQL
,$seqname);
210 $holdtransOK = $this->_transOK
; // save the current status
211 $rs = @$this->Execute($getnext);
213 if ($holdtransOK) $this->_transOK
= true; //if the status was ok before reset
214 $u = strtoupper($seqname);
215 $this->Execute(sprintf($this->_genSeqSQL
,$seqname));
216 $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL
,$seqname));
217 if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL
,$seqname,$startID-1));
218 $rs = $this->Execute($getnext);
222 $this->genID
= mysql_insert_id($this->_connectionID
);
227 $this->_logsql
= $savelog;
231 function &MetaDatabases()
233 $qid = mysql_list_dbs($this->_connectionID
);
236 $max = mysql_num_rows($qid);
238 $db = mysql_tablename($qid,$i);
239 if ($db != 'mysql') $arr[] = $db;
246 // Format date column in sql string given an input format that understands Y M D
247 function SQLDate($fmt, $col=false)
249 if (!$col) $col = $this->sysTimeStamp
;
250 $s = 'DATE_FORMAT('.$col.",'";
253 for ($i=0; $i < $len; $i++
) {
260 $ch = substr($fmt,$i,1);
286 $s .= "'),Quarter($col)";
288 if ($len > $i+
1) $s .= ",DATE_FORMAT($col,'";
328 if ($concat) $s = "CONCAT($s)";
333 // returns concatenated string
334 // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
338 $arr = func_get_args();
340 // suggestion by andrew005@mnogo.ru
341 $s = implode(',',$arr);
342 if (strlen($s) > 0) return "CONCAT($s)";
346 function OffsetDate($dayFraction,$date=false)
348 if (!$date) $date = $this->sysDate
;
350 $fraction = $dayFraction * 24 * 3600;
351 return $date . ' + INTERVAL ' . $fraction.' SECOND';
353 // return "from_unixtime(unix_timestamp($date)+$fraction)";
356 // returns true or false
357 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
359 if (!empty($this->port
)) $argHostname .= ":".$this->port
;
361 if (ADODB_PHPVER
>= 0x4300)
362 $this->_connectionID
= mysql_connect($argHostname,$argUsername,$argPassword,
363 $this->forceNewConnect
,$this->clientFlags
);
364 else if (ADODB_PHPVER
>= 0x4200)
365 $this->_connectionID
= mysql_connect($argHostname,$argUsername,$argPassword,
366 $this->forceNewConnect
);
368 $this->_connectionID
= mysql_connect($argHostname,$argUsername,$argPassword);
370 if ($this->_connectionID
=== false) return false;
371 if ($argDatabasename) return $this->SelectDB($argDatabasename);
375 // returns true or false
376 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
378 if (!empty($this->port
)) $argHostname .= ":".$this->port
;
380 if (ADODB_PHPVER
>= 0x4300)
381 $this->_connectionID
= mysql_pconnect($argHostname,$argUsername,$argPassword,$this->clientFlags
);
383 $this->_connectionID
= mysql_pconnect($argHostname,$argUsername,$argPassword);
384 if ($this->_connectionID
=== false) return false;
385 if ($this->autoRollback
) $this->RollbackTrans();
386 if ($argDatabasename) return $this->SelectDB($argDatabasename);
390 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
392 $this->forceNewConnect
= true;
393 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
396 function &MetaColumns($table)
398 $this->_findschema($table,$schema);
400 $dbName = $this->database
;
401 $this->SelectDB($schema);
403 global $ADODB_FETCH_MODE;
404 $save = $ADODB_FETCH_MODE;
405 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
407 if ($this->fetchMode
!== false) $savem = $this->SetFetchMode(false);
408 $rs = $this->Execute(sprintf($this->metaColumnsSQL
,$table));
411 $this->SelectDB($dbName);
414 if (isset($savem)) $this->SetFetchMode($savem);
415 $ADODB_FETCH_MODE = $save;
416 if (!is_object($rs)) {
423 $fld = new ADOFieldObject();
424 $fld->name
= $rs->fields
[0];
425 $type = $rs->fields
[1];
427 // split type into type(length):
429 if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
430 $fld->type
= $query_array[1];
431 $fld->max_length
= is_numeric($query_array[2]) ?
$query_array[2] : -1;
432 $fld->scale
= is_numeric($query_array[3]) ?
$query_array[3] : -1;
433 } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
434 $fld->type
= $query_array[1];
435 $fld->max_length
= is_numeric($query_array[2]) ?
$query_array[2] : -1;
436 } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
437 $fld->type
= $query_array[1];
438 $arr = explode(",",$query_array[2]);
440 $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
441 $fld->max_length
= ($zlen > 0) ?
$zlen : 1;
444 $fld->max_length
= -1;
446 $fld->not_null
= ($rs->fields
[2] != 'YES');
447 $fld->primary_key
= ($rs->fields
[3] == 'PRI');
448 $fld->auto_increment
= (strpos($rs->fields
[5], 'auto_increment') !== false);
449 $fld->binary
= (strpos($type,'blob') !== false);
450 $fld->unsigned
= (strpos($type,'unsigned') !== false);
451 $fld->zerofill
= (strpos($type,'zerofill') !== false);
455 if ($d != '' && $d != 'NULL') {
456 $fld->has_default
= true;
457 $fld->default_value
= $d;
459 $fld->has_default
= false;
463 if ($save == ADODB_FETCH_NUM
) {
466 $retarr[strtoupper($fld->name
)] = $fld;
475 // returns true or false
476 function SelectDB($dbName)
478 $this->database
= $dbName;
479 $this->databaseName
= $dbName; # obsolete, retained for compat with older adodb versions
480 if ($this->_connectionID
) {
481 return @mysql_select_db
($dbName,$this->_connectionID
);
486 // parameters use PostgreSQL convention, not MySQL
487 function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0)
489 $offsetStr =($offset>=0) ?
((integer)$offset)."," : '';
490 // jason judge, see http://phplens.com/lens/lensforum/msgs.php?id=9220
491 if ($nrows < 0) $nrows = '18446744073709551615';
494 $rs =& $this->CacheExecute($secs,$sql." LIMIT $offsetStr".((integer)$nrows),$inputarr);
496 $rs =& $this->Execute($sql." LIMIT $offsetStr".((integer)$nrows),$inputarr);
500 // returns queryID or false
501 function _query($sql,$inputarr)
503 //global $ADODB_COUNTRECS;
504 //if($ADODB_COUNTRECS)
505 return mysql_query($sql,$this->_connectionID
);
506 //else return @mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6
509 /* Returns: the last error message from previous database operation */
513 if ($this->_logsql
) return $this->_errorMsg
;
514 if (empty($this->_connectionID
)) $this->_errorMsg
= @mysql_error
();
515 else $this->_errorMsg
= @mysql_error
($this->_connectionID
);
516 return $this->_errorMsg
;
519 /* Returns: the last error number from previous database operation */
522 if ($this->_logsql
) return $this->_errorCode
;
523 if (empty($this->_connectionID
)) return @mysql_errno
();
524 else return @mysql_errno
($this->_connectionID
);
527 // returns true or false
530 @mysql_close
($this->_connectionID
);
531 $this->_connectionID
= false;
536 * Maximum size of C field
544 * Maximum size of X field
551 // "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx>
552 function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE )
554 global $ADODB_FETCH_MODE;
555 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC ||
$this->fetchMode
== ADODB_FETCH_ASSOC
) $associative = true;
557 if ( !empty($owner) ) {
558 $table = "$owner.$table";
560 $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
561 if ($associative) $create_sql = $a_create_table["Create Table"];
562 else $create_sql = $a_create_table[1];
566 if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
567 $foreign_keys = array();
568 $num_keys = count($matches[0]);
569 for ( $i = 0; $i < $num_keys; $i ++
) {
570 $my_field = explode('`, `', $matches[1][$i]);
571 $ref_table = $matches[2][$i];
572 $ref_field = explode('`, `', $matches[3][$i]);
575 $ref_table = strtoupper($ref_table);
578 $foreign_keys[$ref_table] = array();
579 $num_fields = count($my_field);
580 for ( $j = 0; $j < $num_fields; $j ++
) {
581 if ( $associative ) {
582 $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
584 $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
589 return $foreign_keys;
595 /*--------------------------------------------------------------------------------------
596 Class Name: Recordset
597 --------------------------------------------------------------------------------------*/
600 class ADORecordSet_mysql
extends ADORecordSet
{
602 var $databaseType = "mysql";
605 function ADORecordSet_mysql($queryID,$mode=false)
607 if ($mode === false) {
608 global $ADODB_FETCH_MODE;
609 $mode = $ADODB_FETCH_MODE;
613 case ADODB_FETCH_NUM
: $this->fetchMode
= MYSQL_NUM
; break;
614 case ADODB_FETCH_ASSOC
:$this->fetchMode
= MYSQL_ASSOC
; break;
615 case ADODB_FETCH_DEFAULT
:
616 case ADODB_FETCH_BOTH
:
618 $this->fetchMode
= MYSQL_BOTH
; break;
620 $this->adodbFetchMode
= $mode;
621 $this->ADORecordSet($queryID);
626 //GLOBAL $ADODB_COUNTRECS;
627 // $this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1;
628 $this->_numOfRows
= @mysql_num_rows
($this->_queryID
);
629 $this->_numOfFields
= @mysql_num_fields
($this->_queryID
);
632 function &FetchField($fieldOffset = -1)
634 if ($fieldOffset != -1) {
635 $o = @mysql_fetch_field
($this->_queryID
, $fieldOffset);
636 $f = @mysql_field_flags
($this->_queryID
,$fieldOffset);
637 $o->max_length
= @mysql_field_len
($this->_queryID
,$fieldOffset); // suggested by: Jim Nicholson (jnich#att.com)
638 //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
639 $o->binary
= (strpos($f,'binary')!== false);
641 else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */
642 $o = @mysql_fetch_field
($this->_queryID
);
643 $o->max_length
= @mysql_field_len
($this->_queryID
); // suggested by: Jim Nicholson (jnich#att.com)
644 //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
650 function &GetRowAssoc($upper=true)
652 if ($this->fetchMode
== MYSQL_ASSOC
&& !$upper) $row = $this->fields
;
653 else $row =& ADORecordSet
::GetRowAssoc($upper);
657 /* Use associative array to get fields array */
658 function Fields($colname)
660 // added @ by "Michael William Miller" <mille562@pilot.msu.edu>
661 if ($this->fetchMode
!= MYSQL_NUM
) return @$this->fields
[$colname];
664 $this->bind
= array();
665 for ($i=0; $i < $this->_numOfFields
; $i++
) {
666 $o = $this->FetchField($i);
667 $this->bind
[strtoupper($o->name
)] = $i;
670 return $this->fields
[$this->bind
[strtoupper($colname)]];
675 if ($this->_numOfRows
== 0) return false;
676 return @mysql_data_seek
($this->_queryID
,$row);
681 //return adodb_movenext($this);
682 //if (defined('ADODB_EXTENSION')) return adodb_movenext($this);
683 if (@$this->fields
= mysql_fetch_array($this->_queryID
,$this->fetchMode
)) {
684 $this->_currentRow +
= 1;
688 $this->_currentRow +
= 1;
696 $this->fields
= @mysql_fetch_array
($this->_queryID
,$this->fetchMode
);
697 return is_array($this->fields
);
701 @mysql_free_result
($this->_queryID
);
702 $this->_queryID
= false;
705 function MetaType($t,$len=-1,$fieldobj=false)
709 $t = $fieldobj->type
;
710 $len = $fieldobj->max_length
;
713 $len = -1; // mysql max_length is not accurate
714 switch (strtoupper($t)) {
722 if ($len <= $this->blobSize
) return 'C';
729 // php_mysql extension always returns 'blob' even if 'text'
730 // so we have to check whether binary...
735 return !empty($fieldobj->binary
) ?
'B' : 'X';
738 case 'DATE': return 'D';
742 case 'TIMESTAMP': return 'T';
751 if (!empty($fieldobj->primary_key
)) return 'R';
760 class ADORecordSet_ext_mysql
extends ADORecordSet_mysql
{
761 function ADORecordSet_ext_mysql($queryID,$mode=false)
763 if ($mode === false) {
764 global $ADODB_FETCH_MODE;
765 $mode = $ADODB_FETCH_MODE;
769 case ADODB_FETCH_NUM
: $this->fetchMode
= MYSQL_NUM
; break;
770 case ADODB_FETCH_ASSOC
:$this->fetchMode
= MYSQL_ASSOC
; break;
771 case ADODB_FETCH_DEFAULT
:
772 case ADODB_FETCH_BOTH
:
774 $this->fetchMode
= MYSQL_BOTH
; break;
776 $this->adodbFetchMode
= $mode;
777 $this->ADORecordSet($queryID);
782 return @adodb_movenext
($this);