3 V4.22 15 Apr 2004 (c) 2000-2004 John Lim (jlim@natsoft.com.my). All rights reserved.
\r
4 Released under both BSD license and Lesser GPL library license.
\r
5 Whenever there is any discrepancy between the two licenses,
\r
6 the BSD license will take precedence.
\r
9 MySQL code that does not support transactions. Use mysqlt if you need transactions.
\r
10 Requires mysql client. Works on Windows and Unix.
\r
12 28 Feb 2001: MetaColumns bug fix - suggested by Freek Dijkstra (phpeverywhere@macfreek.com)
\r
15 if (! defined("_ADODB_MYSQL_LAYER")) {
\r
16 define("_ADODB_MYSQL_LAYER", 1 );
\r
18 class ADODB_mysql extends ADOConnection {
\r
19 var $databaseType = 'mysql';
\r
20 var $dataProvider = 'mysql';
\r
21 var $hasInsertID = true;
\r
22 var $hasAffectedRows = true;
\r
23 var $metaTablesSQL = "SHOW TABLES";
\r
24 var $metaColumnsSQL = "SHOW COLUMNS FROM %s";
\r
25 var $fmtTimeStamp = "'Y-m-d H:i:s'";
\r
26 var $hasLimit = true;
\r
27 var $hasMoveFirst = true;
\r
28 var $hasGenID = true;
\r
29 var $upperCase = 'upper';
\r
30 var $isoDates = true; // accepts dates in ISO format
\r
31 var $sysDate = 'CURDATE()';
\r
32 var $sysTimeStamp = 'NOW()';
\r
33 var $hasTransactions = false;
\r
34 var $forceNewConnect = false;
\r
35 var $poorAffectedRows = true;
\r
36 var $clientFlags = 0;
\r
37 var $substr = "substring";
\r
38 var $nameQuote = '`'; /// string to use to quote identifiers and names
\r
40 function ADODB_mysql()
\r
44 function ServerInfo()
\r
46 $arr['description'] = ADOConnection::GetOne("select version()");
\r
47 $arr['version'] = ADOConnection::_findvers($arr['description']);
\r
51 function IfNull( $field, $ifNull )
\r
53 return " IFNULL($field, $ifNull) "; // if MySQL
\r
56 function &MetaTables($ttype=false,$showSchema=false,$mask=false)
\r
59 $save = $this->metaTablesSQL;
\r
60 $mask = $this->qstr($mask);
\r
61 $this->metaTablesSQL .= " like $mask";
\r
63 $ret =& ADOConnection::MetaTables($ttype,$showSchema);
\r
66 $this->metaTablesSQL = $save;
\r
72 function &MetaIndexes ($table, $primary = FALSE, $owner=false)
\r
74 // save old fetch mode
\r
75 global $ADODB_FETCH_MODE;
\r
77 $save = $ADODB_FETCH_MODE;
\r
78 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
\r
79 if ($this->fetchMode !== FALSE) {
\r
80 $savem = $this->SetFetchMode(FALSE);
\r
83 // get index details
\r
84 $rs = $this->Execute(sprintf('SHOW INDEX FROM %s',$table));
\r
86 // restore fetchmode
\r
87 if (isset($savem)) {
\r
88 $this->SetFetchMode($savem);
\r
90 $ADODB_FETCH_MODE = $save;
\r
92 if (!is_object($rs)) {
\r
96 $indexes = array ();
\r
98 // parse index data into array
\r
99 while ($row = $rs->FetchRow()) {
\r
100 if ($primary == FALSE AND $row[2] == 'PRIMARY') {
\r
104 if (!isset($indexes[$row[2]])) {
\r
105 $indexes[$row[2]] = array(
\r
106 'unique' => ($row[1] == 0),
\r
107 'columns' => array()
\r
111 $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
\r
114 // sort columns by order in the index
\r
115 foreach ( array_keys ($indexes) as $index )
\r
117 ksort ($indexes[$index]['columns']);
\r
124 // if magic quotes disabled, use mysql_real_escape_string()
\r
125 function qstr($s,$magic_quotes=false)
\r
127 if (!$magic_quotes) {
\r
129 if (ADODB_PHPVER >= 0x4300) {
\r
130 if (is_resource($this->_connectionID))
\r
131 return "'".mysql_real_escape_string($s,$this->_connectionID)."'";
\r
133 if ($this->replaceQuote[0] == '\\'){
\r
134 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
\r
136 return "'".str_replace("'",$this->replaceQuote,$s)."'";
\r
139 // undo magic quotes for "
\r
140 $s = str_replace('\\"','"',$s);
\r
144 function _insertid()
\r
146 return mysql_insert_id($this->_connectionID);
\r
149 function GetOne($sql,$inputarr=false)
\r
151 $rs =& $this->SelectLimit($sql,1,-1,$inputarr);
\r
154 if ($rs->EOF) return false;
\r
155 return reset($rs->fields);
\r
161 function _affectedrows()
\r
163 return mysql_affected_rows($this->_connectionID);
\r
166 // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html
\r
167 // Reference on Last_Insert_ID on the recommended way to simulate sequences
\r
168 var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
\r
169 var $_genSeqSQL = "create table %s (id int not null)";
\r
170 var $_genSeq2SQL = "insert into %s values (%s)";
\r
171 var $_dropSeqSQL = "drop table %s";
\r
173 function CreateSequence($seqname='adodbseq',$startID=1)
\r
175 if (empty($this->_genSeqSQL)) return false;
\r
176 $u = strtoupper($seqname);
\r
178 $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
\r
179 if (!$ok) return false;
\r
180 return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
\r
184 function GenID($seqname='adodbseq',$startID=1)
\r
186 // post-nuke sets hasGenID to false
\r
187 if (!$this->hasGenID) return false;
\r
189 $savelog = $this->_logsql;
\r
190 $this->_logsql = false;
\r
191 $getnext = sprintf($this->_genIDSQL,$seqname);
\r
192 $holdtransOK = $this->_transOK; // save the current status
\r
193 $rs = @$this->Execute($getnext);
\r
195 if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
\r
196 $u = strtoupper($seqname);
\r
197 $this->Execute(sprintf($this->_genSeqSQL,$seqname));
\r
198 $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
\r
199 $rs = $this->Execute($getnext);
\r
201 $this->genID = mysql_insert_id($this->_connectionID);
\r
203 if ($rs) $rs->Close();
\r
205 $this->_logsql = $savelog;
\r
206 return $this->genID;
\r
209 function &MetaDatabases()
\r
211 $qid = mysql_list_dbs($this->_connectionID);
\r
214 $max = mysql_num_rows($qid);
\r
215 while ($i < $max) {
\r
216 $db = mysql_tablename($qid,$i);
\r
217 if ($db != 'mysql') $arr[] = $db;
\r
224 // Format date column in sql string given an input format that understands Y M D
\r
225 function SQLDate($fmt, $col=false)
\r
227 if (!$col) $col = $this->sysTimeStamp;
\r
228 $s = 'DATE_FORMAT('.$col.",'";
\r
230 $len = strlen($fmt);
\r
231 for ($i=0; $i < $len; $i++) {
\r
240 $s .= "'),Quarter($col)";
\r
242 if ($len > $i+1) $s .= ",DATE_FORMAT($col,'";
\r
283 $ch = substr($fmt,$i,1);
\r
290 if ($concat) $s = "CONCAT($s)";
\r
295 // returns concatenated string
\r
296 // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
\r
300 $arr = func_get_args();
\r
302 // suggestion by andrew005@mnogo.ru
\r
303 $s = implode(',',$arr);
\r
304 if (strlen($s) > 0) return "CONCAT($s)";
\r
308 function OffsetDate($dayFraction,$date=false)
\r
310 if (!$date) $date = $this->sysDate;
\r
311 return "from_unixtime(unix_timestamp($date)+($dayFraction)*24*3600)";
\r
314 // returns true or false
\r
315 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
\r
317 if (ADODB_PHPVER >= 0x4300)
\r
318 $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword,
\r
319 $this->forceNewConnect,$this->clientFlags);
\r
320 else if (ADODB_PHPVER >= 0x4200)
\r
321 $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword,
\r
322 $this->forceNewConnect);
\r
324 $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword);
\r
326 if ($this->_connectionID === false) return false;
\r
327 if ($argDatabasename) return $this->SelectDB($argDatabasename);
\r
331 // returns true or false
\r
332 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
\r
334 if (ADODB_PHPVER >= 0x4300)
\r
335 $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword,$this->clientFlags);
\r
337 $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword);
\r
338 if ($this->_connectionID === false) return false;
\r
339 if ($this->autoRollback) $this->RollbackTrans();
\r
340 if ($argDatabasename) return $this->SelectDB($argDatabasename);
\r
344 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
\r
346 $this->forceNewConnect = true;
\r
347 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
\r
350 function &MetaColumns($table)
\r
353 if ($this->metaColumnsSQL) {
\r
354 global $ADODB_FETCH_MODE;
\r
356 $save = $ADODB_FETCH_MODE;
\r
357 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
\r
358 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
\r
360 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
\r
362 if (isset($savem)) $this->SetFetchMode($savem);
\r
363 $ADODB_FETCH_MODE = $save;
\r
365 if ($rs === false) return false;
\r
369 $fld = new ADOFieldObject();
\r
370 $fld->name = $rs->fields[0];
\r
371 $type = $rs->fields[1];
\r
374 // split type into type(length):
\r
375 $fld->scale = null;
\r
376 if (strpos($type,',') && preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
\r
377 $fld->type = $query_array[1];
\r
378 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
\r
379 $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
\r
380 } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
\r
381 $fld->type = $query_array[1];
\r
382 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
\r
384 $fld->max_length = -1;
\r
385 $fld->type = $type;
\r
388 // split type into type(length):
\r
389 if (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
\r
390 $fld->type = $query_array[1];
\r
391 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
\r
393 $fld->max_length = -1;
\r
394 $fld->type = $type;
\r
396 $fld->not_null = ($rs->fields[2] != 'YES');
\r
397 $fld->primary_key = ($rs->fields[3] == 'PRI');
\r
398 $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
\r
399 $fld->binary = (strpos($fld->type,'blob') !== false);
\r
401 if (!$fld->binary) {
\r
402 $d = $rs->fields[4];
\r
403 if ($d != "" && $d != "NULL") {
\r
404 $fld->has_default = true;
\r
405 $fld->default_value = $d;
\r
407 $fld->has_default = false;
\r
410 if ($save == ADODB_FETCH_NUM) $retarr[] = $fld;
\r
411 else $retarr[strtoupper($fld->name)] = $fld;
\r
420 // returns true or false
\r
421 function SelectDB($dbName)
\r
423 $this->databaseName = $dbName;
\r
424 if ($this->_connectionID) {
\r
425 return @mysql_select_db($dbName,$this->_connectionID);
\r
427 else return false;
\r
430 // parameters use PostgreSQL convention, not MySQL
\r
431 function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0)
\r
433 $offsetStr =($offset>=0) ? "$offset," : '';
\r
436 $rs =& $this->CacheExecute($secs,$sql." LIMIT $offsetStr$nrows",$inputarr);
\r
438 $rs =& $this->Execute($sql." LIMIT $offsetStr$nrows",$inputarr);
\r
443 // returns queryID or false
\r
444 function _query($sql,$inputarr)
\r
446 //global $ADODB_COUNTRECS;
\r
447 //if($ADODB_COUNTRECS)
\r
448 return mysql_query($sql,$this->_connectionID);
\r
449 //else return @mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6
\r
452 /* Returns: the last error message from previous database operation */
\r
453 function ErrorMsg()
\r
456 if ($this->_logsql) return $this->_errorMsg;
\r
457 if (empty($this->_connectionID)) $this->_errorMsg = @mysql_error();
\r
458 else $this->_errorMsg = @mysql_error($this->_connectionID);
\r
459 return $this->_errorMsg;
\r
462 /* Returns: the last error number from previous database operation */
\r
463 function ErrorNo()
\r
465 if ($this->_logsql) return $this->_errorCode;
\r
466 if (empty($this->_connectionID)) return @mysql_errno();
\r
467 else return @mysql_errno($this->_connectionID);
\r
472 // returns true or false
\r
475 @mysql_close($this->_connectionID);
\r
476 $this->_connectionID = false;
\r
481 * Maximum size of C field
\r
489 * Maximum size of X field
\r
493 return 4294967295;
\r
498 /*--------------------------------------------------------------------------------------
\r
499 Class Name: Recordset
\r
500 --------------------------------------------------------------------------------------*/
\r
502 class ADORecordSet_mysql extends ADORecordSet{
\r
504 var $databaseType = "mysql";
\r
505 var $canSeek = true;
\r
507 function ADORecordSet_mysql($queryID,$mode=false)
\r
509 if ($mode === false) {
\r
510 global $ADODB_FETCH_MODE;
\r
511 $mode = $ADODB_FETCH_MODE;
\r
515 case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break;
\r
516 case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break;
\r
518 case ADODB_FETCH_DEFAULT:
\r
519 case ADODB_FETCH_BOTH:$this->fetchMode = MYSQL_BOTH; break;
\r
522 $this->ADORecordSet($queryID);
\r
527 //GLOBAL $ADODB_COUNTRECS;
\r
528 // $this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1;
\r
529 $this->_numOfRows = @mysql_num_rows($this->_queryID);
\r
530 $this->_numOfFields = @mysql_num_fields($this->_queryID);
\r
533 function &FetchField($fieldOffset = -1)
\r
536 if ($fieldOffset != -1) {
\r
537 $o = @mysql_fetch_field($this->_queryID, $fieldOffset);
\r
538 $f = @mysql_field_flags($this->_queryID,$fieldOffset);
\r
539 $o->max_length = @mysql_field_len($this->_queryID,$fieldOffset); // suggested by: Jim Nicholson (jnich@att.com)
\r
540 //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
\r
541 $o->binary = (strpos($f,'binary')!== false);
\r
543 else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */
\r
544 $o = @mysql_fetch_field($this->_queryID);
\r
545 $o->max_length = @mysql_field_len($this->_queryID); // suggested by: Jim Nicholson (jnich@att.com)
\r
546 //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
\r
552 function &GetRowAssoc($upper=true)
\r
554 if ($this->fetchMode == MYSQL_ASSOC && !$upper) return $this->fields;
\r
555 $row =& ADORecordSet::GetRowAssoc($upper);
\r
559 /* Use associative array to get fields array */
\r
560 function Fields($colname)
\r
562 // added @ by "Michael William Miller" <mille562@pilot.msu.edu>
\r
563 if ($this->fetchMode != MYSQL_NUM) return @$this->fields[$colname];
\r
565 if (!$this->bind) {
\r
566 $this->bind = array();
\r
567 for ($i=0; $i < $this->_numOfFields; $i++) {
\r
568 $o = $this->FetchField($i);
\r
569 $this->bind[strtoupper($o->name)] = $i;
\r
572 return $this->fields[$this->bind[strtoupper($colname)]];
\r
575 function _seek($row)
\r
577 if ($this->_numOfRows == 0) return false;
\r
578 return @mysql_data_seek($this->_queryID,$row);
\r
582 // 10% speedup to move MoveNext to child class
\r
583 function MoveNext()
\r
585 //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return adodb_movenext($this);
\r
587 if ($this->EOF) return false;
\r
589 $this->_currentRow++;
\r
590 $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode);
\r
591 if (is_array($this->fields)) return true;
\r
595 /* -- tested raising an error -- appears pointless
\r
596 $conn = $this->connection;
\r
597 if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
\r
598 $fn = $conn->raiseErrorFn;
\r
599 $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
\r
607 $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode);
\r
608 return is_array($this->fields);
\r
611 function _close() {
\r
612 @mysql_free_result($this->_queryID);
\r
613 $this->_queryID = false;
\r
616 function MetaType($t,$len=-1,$fieldobj=false)
\r
618 if (is_object($t)) {
\r
620 $t = $fieldobj->type;
\r
621 $len = $fieldobj->max_length;
\r
624 $len = -1; // mysql max_length is not accurate
\r
625 switch (strtoupper($t)) {
\r
633 if ($len <= $this->blobSize) return 'C';
\r
640 // php_mysql extension always returns 'blob' even if 'text'
\r
641 // so we have to check whether binary...
\r
646 return !empty($fieldobj->binary) ? 'B' : 'X';
\r
649 case 'DATE': return 'D';
\r
653 case 'TIMESTAMP': return 'T';
\r
662 if (!empty($fieldobj->primary_key)) return 'R';
\r
665 default: return 'N';
\r