EOL standardization fixes
[openemr.git] / library / adodb / drivers / adodb-mysqli.inc.php
blob28ecd5947933d3fc5fe10cace728da8a05868e85
1 <?php
2 /*
3 V4.20 22 Feb 2004 (c) 2000-2004 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.
7 Set tabs to 8.
9 MySQL code that does not support transactions. Use mysqlt if you need transactions.
10 Requires mysql client. Works on Windows and Unix.
12 21 October 2003: MySQLi extension implementation by Arjen de Rijke (a.de.rijke@xs4all.nl)
13 Based on adodb 3.40
14 */
15 if (! defined("_ADODB_MYSQL_LAYER")) {
16 define("_ADODB_MYSQL_LAYER", 1 );
18 class ADODB_mysqli extends ADOConnection {
19 var $databaseType = 'mysqli';
20 var $dataProvider = 'native';
21 var $hasInsertID = true;
22 var $hasAffectedRows = true;
23 var $metaTablesSQL = "SHOW TABLES";
24 var $metaColumnsSQL = "SHOW COLUMNS FROM %s";
25 var $fmtTimeStamp = "'Y-m-d H:i:s'";
26 var $hasLimit = true;
27 var $hasMoveFirst = true;
28 var $hasGenID = true;
29 var $upperCase = 'upper';
30 var $isoDates = true; // accepts dates in ISO format
31 var $sysDate = 'CURDATE()';
32 var $sysTimeStamp = 'NOW()';
33 var $hasTransactions = false;
34 var $forceNewConnect = false;
35 var $poorAffectedRows = true;
36 var $clientFlags = 0;
37 var $executeOnly = true;
38 var $substr = "substring";
39 var $nameQuote = '`'; /// string to use to quote identifiers and names
40 //var $_bindInputArray = true;
42 function ADODB_mysqli()
44 if(!extension_loaded("mysqli"))
46 trigger_error("You must have the MySQLi extension.", E_USER_ERROR);
50 function IfNull( $field, $ifNull )
52 return " IFNULL($field, $ifNull) "; // if MySQL
55 function ServerInfo()
57 $arr['description'] = $this->GetOne("select version()");
58 $arr['version'] = ADOConnection::_findvers($arr['description']);
59 return $arr;
63 function BeginTrans()
65 if ($this->transOff) return true;
66 $this->transCnt += 1;
67 $this->Execute('SET AUTOCOMMIT=0');
68 $this->Execute('BEGIN');
69 return true;
72 function CommitTrans($ok=true)
74 if ($this->transOff) return true;
75 if (!$ok) return $this->RollbackTrans();
77 if ($this->transCnt) $this->transCnt -= 1;
78 $this->Execute('COMMIT');
79 $this->Execute('SET AUTOCOMMIT=1');
80 return true;
83 function RollbackTrans()
85 if ($this->transOff) return true;
86 if ($this->transCnt) $this->transCnt -= 1;
87 $this->Execute('ROLLBACK');
88 $this->Execute('SET AUTOCOMMIT=1');
89 return true;
92 // if magic quotes disabled, use mysql_real_escape_string()
93 // From readme.htm:
94 // Quotes a string to be sent to the database. The $magic_quotes_enabled
95 // parameter may look funny, but the idea is if you are quoting a
96 // string extracted from a POST/GET variable, then
97 // pass get_magic_quotes_gpc() as the second parameter. This will
98 // ensure that the variable is not quoted twice, once by qstr and once
99 // by the magic_quotes_gpc.
101 //Eg. $s = $db->qstr(HTTP_GET_VARS['name'],get_magic_quotes_gpc());
102 function qstr($s, $magic_quotes = false)
104 if (!$magic_quotes) {
105 if (ADODB_PHPVER >= 0x5000) {
106 // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
107 return "'" . mysqli_real_escape_string($this->_connectionID, $s) . "'";
109 else
111 trigger_error("phpver < 5 not implemented", E_USER_ERROR);
114 if ($this->replaceQuote[0] == '\\')
116 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
118 return "'".str_replace("'",$this->replaceQuote,$s)."'";
120 // undo magic quotes for "
121 $s = str_replace('\\"','"',$s);
122 return "'$s'";
125 function _insertid()
127 // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
128 $result = @mysqli_insert_id($this->_connectionID);
129 if ($result == -1){
130 if ($this->debug) ADOConnection::outp("mysqli_insert_id() failed : " . $this->ErrorMsg());
132 return $result;
135 // Only works for INSERT, UPDATE and DELETE query's
136 function _affectedrows()
138 // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
139 $result = @mysqli_affected_rows($this->_connectionID);
140 if ($result == -1) {
141 if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : " . $this->ErrorMsg());
143 return $result;
146 // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html
147 // Reference on Last_Insert_ID on the recommended way to simulate sequences
148 var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
149 var $_genSeqSQL = "create table %s (id int not null)";
150 var $_genSeq2SQL = "insert into %s values (%s)";
151 var $_dropSeqSQL = "drop table %s";
153 function CreateSequence($seqname='adodbseq',$startID=1)
155 if (empty($this->_genSeqSQL)) return false;
156 $u = strtoupper($seqname);
158 $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
159 if (!$ok) return false;
160 return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
163 function GenID($seqname='adodbseq',$startID=1)
165 // post-nuke sets hasGenID to false
166 if (!$this->hasGenID) return false;
168 $getnext = sprintf($this->_genIDSQL,$seqname);
169 $holdtransOK = $this->_transOK; // save the current status
170 $rs = @$this->Execute($getnext);
171 if (!$rs) {
172 if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
173 $u = strtoupper($seqname);
174 $this->Execute(sprintf($this->_genSeqSQL,$seqname));
175 $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
176 $rs = $this->Execute($getnext);
178 $this->genID = mysqli_insert_id($this->_connectionID);
180 if ($rs) $rs->Close();
182 return $this->genID;
185 function &MetaDatabases()
187 $query = "SHOW DATABASES";
188 $ret =& $this->Execute($query);
189 return $ret;
193 function &MetaIndexes ($table, $primary = FALSE)
195 // save old fetch mode
196 global $ADODB_FETCH_MODE;
198 $save = $ADODB_FETCH_MODE;
199 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
200 if ($this->fetchMode !== FALSE) {
201 $savem = $this->SetFetchMode(FALSE);
204 // get index details
205 $rs = $this->Execute(sprintf('SHOW INDEXES FROM %s',$table));
207 // restore fetchmode
208 if (isset($savem)) {
209 $this->SetFetchMode($savem);
211 $ADODB_FETCH_MODE = $save;
213 if (!is_object($rs)) {
214 return FALSE;
217 $indexes = array ();
219 // parse index data into array
220 while ($row = $rs->FetchRow()) {
221 if ($primary == FALSE AND $row[2] == 'PRIMARY') {
222 continue;
225 if (!isset($indexes[$row[2]])) {
226 $indexes[$row[2]] = array(
227 'unique' => ($row[1] == 0),
228 'columns' => array()
232 $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
235 // sort columns by order in the index
236 foreach ( array_keys ($indexes) as $index )
238 ksort ($indexes[$index]['columns']);
241 return $indexes;
245 // Format date column in sql string given an input format that understands Y M D
246 function SQLDate($fmt, $col=false)
248 if (!$col) $col = $this->sysTimeStamp;
249 $s = 'DATE_FORMAT('.$col.",'";
250 $concat = false;
251 $len = strlen($fmt);
252 for ($i=0; $i < $len; $i++) {
253 $ch = $fmt[$i];
254 switch($ch) {
255 case 'Y':
256 case 'y':
257 $s .= '%Y';
258 break;
259 case 'Q':
260 case 'q':
261 $s .= "'),Quarter($col)";
263 if ($len > $i+1) $s .= ",DATE_FORMAT($col,'";
264 else $s .= ",('";
265 $concat = true;
266 break;
267 case 'M':
268 $s .= '%b';
269 break;
271 case 'm':
272 $s .= '%m';
273 break;
274 case 'D':
275 case 'd':
276 $s .= '%d';
277 break;
279 case 'H':
280 $s .= '%H';
281 break;
283 case 'h':
284 $s .= '%I';
285 break;
287 case 'i':
288 $s .= '%i';
289 break;
291 case 's':
292 $s .= '%s';
293 break;
295 case 'a':
296 case 'A':
297 $s .= '%p';
298 break;
300 default:
302 if ($ch == '\\') {
303 $i++;
304 $ch = substr($fmt,$i,1);
306 $s .= $ch;
307 break;
310 $s.="')";
311 if ($concat) $s = "CONCAT($s)";
312 return $s;
315 // returns concatenated string
316 // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
317 function Concat()
319 $s = "";
320 $arr = func_get_args();
322 // suggestion by andrew005@mnogo.ru
323 $s = implode(',',$arr);
324 if (strlen($s) > 0) return "CONCAT($s)";
325 else return '';
328 // dayFraction is a day in floating point
329 function OffsetDate($dayFraction,$date=false)
331 if (!$date)
332 $date = $this->sysDate;
333 return "from_unixtime(unix_timestamp($date)+($dayFraction)*24*3600)";
336 // returns true or false
337 // To add: parameter int $port,
338 // parameter string $socket
339 function _connect($argHostname = NULL,
340 $argUsername = NULL,
341 $argPassword = NULL,
342 $argDatabasename = NULL)
344 // @ means: error surpression on
345 $this->_connectionID = @mysqli_init();
347 if (is_null($this->_connectionID))
349 // mysqli_init only fails if insufficient memory
350 if ($this->debug)
351 ADOConnection::outp("mysqli_init() failed : " . $this->ErrorMsg());
352 return false;
354 // Set connection options
355 // Not implemented now
356 // mysqli_options($this->_connection,,);
357 if (mysqli_real_connect($this->_connectionID,
358 $argHostname,
359 $argUsername,
360 $argPassword,
361 $argDatabasename))
363 if ($argDatabasename)
365 return $this->SelectDB($argDatabasename);
368 return true;
370 else
372 if ($this->debug)
373 ADOConnection::outp("Could't connect : " . $this->ErrorMsg());
374 return false;
378 // returns true or false
379 // How to force a persistent connection
380 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
382 // not implemented in mysqli (yet)?
383 $this->_connectionID = mysqli_connect($argHostname,
384 $argUsername,
385 $argPassword,
386 $argDatabasename);
387 if ($this->_connectionID === false) return false;
388 // if ($this->autoRollback) $this->RollbackTrans();
389 if ($argDatabasename) return $this->SelectDB($argDatabasename);
390 return true;
393 // When is this used? Close old connection first?
394 // In _connect(), check $this->forceNewConnect?
395 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
397 $this->forceNewConnect = true;
398 $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
401 function &MetaColumns($table)
403 if ($this->metaColumnsSQL) {
404 global $ADODB_FETCH_MODE;
405 $save = $ADODB_FETCH_MODE;
406 $rs = false;
407 switch($ADODB_FETCH_MODE)
409 case ADODB_FETCH_NUM:
410 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
411 $rs = $this->Execute(sprintf($this->metaColumnsSQL,
412 $table));
414 $ADODB_FETCH_MODE = $save;
415 if ($rs === false) break;
416 $retarr = array();
417 while (!$rs->EOF){
418 $fld = new ADOFieldObject();
419 $fld->name = $rs->fields[0];
420 $fld->type = $rs->fields[1];
421 // split type into type(length):
422 if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array))
424 $fld->type = $query_array[1];
425 $fld->max_length = $query_array[2];
427 else
429 $fld->max_length = -1;
431 $fld->not_null = ($rs->fields[2] != 'YES');
432 $fld->primary_key = ($rs->fields[3] == 'PRI');
433 $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
434 $fld->binary = (strpos($fld->type,'blob') !== false);
435 if (!$fld->binary)
437 $d = $rs->fields[4];
438 $d = $rs->fields['Default'];
439 if ($d != "" && $d != "NULL")
441 $fld->has_default = true;
442 $fld->default_value = $d;
444 else
446 $fld->has_default = false;
449 $retarr[strtoupper($fld->name)] = $fld;
450 $rs->MoveNext();
452 break;
453 case ADODB_FETCH_ASSOC:
454 case ADODB_FETCH_DEFAULT:
455 case ADODB_FETCH_BOTH:
456 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
457 $rs = $this->Execute(sprintf($this->metaColumnsSQL,
458 $table));
459 $ADODB_FETCH_MODE = $save;
460 if ($rs === false) break;
461 $retarr = array();
462 while (!$rs->EOF){
463 $fld = new ADOFieldObject();
464 $fld->name = $rs->fields['Field'];
465 $fld->type = $rs->fields['Type'];
467 // split type into type(length):
468 if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array))
470 $fld->type = $query_array[1];
471 $fld->max_length = $query_array[2];
473 else
475 $fld->max_length = -1;
477 $fld->not_null = ($rs->fields['Null'] != 'YES');
478 $fld->primary_key = ($rs->fields['Key'] == 'PRI');
479 $fld->auto_increment = (strpos($rs->fields['Extra'], 'auto_increment') !== false);
480 $fld->binary = (strpos($fld->type,'blob') !== false);
481 if (!$fld->binary)
483 $d = $rs->fields['Default'];
484 if ($d != "" && $d != "NULL")
486 $fld->has_default = true;
487 $fld->default_value = $d;
489 else
491 $fld->has_default = false;
494 $retarr[strtoupper($fld->name)] = $fld;
495 $rs->MoveNext();
497 break;
498 default:
501 if ($rs === false) return false;
502 $rs->Close();
503 return $retarr;
505 return false;
508 // returns true or false
509 function SelectDB($dbName)
511 // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
512 $this->databaseName = $dbName;
513 if ($this->_connectionID) {
514 $result = @mysqli_select_db($this->_connectionID, $dbName);
515 if (!$result) {
516 ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg());
518 return $result;
520 return false;
523 // parameters use PostgreSQL convention, not MySQL
524 function &SelectLimit($sql,
525 $nrows = -1,
526 $offset = -1,
527 $inputarr = false,
528 $arg3 = false,
529 $secs = 0)
531 $offsetStr = ($offset >= 0) ? "$offset," : '';
533 if ($secs)
534 $rs =& $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr , $arg3);
535 else
536 $rs =& $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr , $arg3);
538 return $rs;
542 function Prepare($sql)
544 return $sql;
546 $stmt = mysqli_prepare($this->_connectionID,$sql);
547 if (!$stmt) return false;
548 return array($sql,$stmt);
552 // returns queryID or false
553 function _query($sql, $inputarr)
555 global $ADODB_COUNTRECS;
557 if (is_array($sql)) {
558 $stmt = $sql[1];
559 foreach($inputarr as $k => $v) {
560 if (is_string($v)) $a[] = MYSQLI_BIND_STRING;
561 else if (is_integer($v)) $a[] = MYSQLI_BIND_INT;
562 else $a[] = MYSQLI_BIND_DOUBLE;
564 $fnarr =& array_merge( array($stmt,$a) , $inputarr);
565 $ret = call_user_func_array('mysqli_bind_param',$fnarr);
567 $ret = mysqli_execute($stmt);
568 return $ret;
570 if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
571 if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
572 return false;
575 return $mysql_res;
578 /* Returns: the last error message from previous database operation */
579 function ErrorMsg()
581 if (empty($this->_connectionID))
582 $this->_errorMsg = @mysqli_error();
583 else
584 $this->_errorMsg = @mysqli_error($this->_connectionID);
585 return $this->_errorMsg;
588 /* Returns: the last error number from previous database operation */
589 function ErrorNo()
591 if (empty($this->_connectionID))
592 return @mysqli_errno();
593 else
594 return @mysqli_errno($this->_connectionID);
597 // returns true or false
598 function _close()
600 @mysqli_close($this->_connectionID);
601 $this->_connectionID = false;
605 * Maximum size of C field
607 function CharMax()
609 return 255;
613 * Maximum size of X field
615 function TextMax()
617 return 4294967295;
623 /*--------------------------------------------------------------------------------------
624 Class Name: Recordset
625 --------------------------------------------------------------------------------------*/
627 class ADORecordSet_mysqli extends ADORecordSet{
629 var $databaseType = "mysqli";
630 var $canSeek = true;
632 function ADORecordSet_mysqli($queryID, $mode = false)
634 if ($mode === false)
636 global $ADODB_FETCH_MODE;
637 $mode = $ADODB_FETCH_MODE;
639 switch ($mode)
641 case ADODB_FETCH_NUM:
642 $this->fetchMode = MYSQLI_NUM;
643 break;
644 case ADODB_FETCH_ASSOC:
645 $this->fetchMode = MYSQLI_ASSOC;
646 break;
647 case ADODB_FETCH_DEFAULT:
648 case ADODB_FETCH_BOTH:
649 default:
650 $this->fetchMode = MYSQLI_ASSOC;
651 break;
653 $this->ADORecordSet($queryID);
656 function _initrs()
658 // mysqli_num_rows only return correct number, depens
659 // on the use of mysql_store_result and mysql_use_result
660 if (!$this->Connection->executeOnly) {
661 $this->_numOfRows = @mysqli_num_rows($this->_queryID);
662 $this->_numOfFields = @mysqli_num_fields($this->_queryID);
664 else {
665 $this->_numOfRows = 0;
666 $this->_numOfFields = 0;
670 function &FetchField($fieldOffset = -1)
672 $fieldnr = $fieldOffset;
673 if ($fieldOffset != -1) {
674 $fieldOffset = mysqi_field_seek($this->_queryID, $fieldnr);
676 $o = mysqli_fetch_field($this->_queryID);
677 return $o;
680 function &GetRowAssoc($upper = true)
682 if ($this->fetchMode == MYSQLI_ASSOC && !$upper)
683 return $this->fields;
684 $row =& ADORecordSet::GetRowAssoc($upper);
685 return $row;
688 /* Use associative array to get fields array */
689 function Fields($colname)
691 if ($this->fetchMode != MYSQLI_NUM)
692 return @$this->fields[$colname];
694 if (!$this->bind) {
695 $this->bind = array();
696 for ($i = 0; $i < $this->_numOfFields; $i++) {
697 $o = $this->FetchField($i);
698 $this->bind[strtoupper($o->name)] = $i;
701 return $this->fields[$this->bind[strtoupper($colname)]];
704 function _seek($row)
706 if ($this->_numOfRows == 0)
707 return false;
709 if ($row < 0)
710 return false;
712 mysqli_data_seek($this->_queryID, $row);
713 $this->EOF = false;
714 return true;
717 // 10% speedup to move MoveNext to child class
718 // This is the only implementation that works now (23-10-2003).
719 // Other functions return no or the wrong results.
720 function MoveNext()
722 if ($this->EOF)
723 return false;
724 $this->_currentRow++;
725 switch($this->fetchMode)
727 case MYSQLI_NUM:
728 $this->fields = mysqli_fetch_array($this->_queryID);
729 break;
730 case MYSQLI_ASSOC:
731 case MYSQLI_BOTH:
732 $this->fields = mysqli_fetch_assoc($this->_queryID);
733 break;
734 default:
736 if (is_array($this->fields))
737 return true;
738 $this->EOF = true;
739 return false;
742 function _fetch()
744 // mysqli_fetch_array($this->_queryID, MYSQLI_NUM) does not
745 // work (22-10-2003). But mysqli_fetch_array($this->_queryID) gives
746 // int resulttype should default to MYSQLI_BOTH,but give MYSQLI_NUM.
748 // $this->fields = mysqli_fetch_fields($this->_queryID);
749 // $this->fields = mysqli_fetch_array($this->_queryID); //, $this->fetchMode);
751 $this->fields = mysqli_fetch_assoc($this->_queryID); // $this->fetchMode);
752 return is_array($this->fields);
755 function _close()
757 mysqli_free_result($this->_queryID);
758 $this->_queryID = false;
761 function MetaType($t, $len = -1, $fieldobj = false)
763 if (is_object($t))
765 $fieldobj = $t;
766 $t = $fieldobj->type;
767 $len = $fieldobj->max_length;
770 $len = -1; // mysql max_length is not accurate
771 switch (strtoupper($t)) {
772 case 'STRING':
773 case 'CHAR':
774 case 'VARCHAR':
775 case 'TINYBLOB':
776 case 'TINYTEXT':
777 case 'ENUM':
778 case 'SET':
779 if ($len <= $this->blobSize) return 'C';
781 case 'TEXT':
782 case 'LONGTEXT':
783 case 'MEDIUMTEXT':
784 return 'X';
786 // php_mysql extension always returns 'blob' even if 'text'
787 // so we have to check whether binary...
788 case 'IMAGE':
789 case 'LONGBLOB':
790 case 'BLOB':
791 case 'MEDIUMBLOB':
792 return !empty($fieldobj->binary) ? 'B' : 'X';
793 case 'YEAR':
794 case 'DATE':
795 return 'D';
797 case 'TIME':
798 case 'DATETIME':
799 case 'TIMESTAMP': return 'T';
801 case 'INT':
802 case 'INTEGER':
803 case 'BIGINT':
804 case 'TINYINT':
805 case 'MEDIUMINT':
806 case 'SMALLINT':
808 if (!empty($fieldobj->primary_key)) return 'R';
809 else return 'I';
810 // Added floating-point types
811 // Maybe not necessery.
812 case 'FLOAT':
813 case 'DOUBLE':
814 // case 'DOUBLE PRECISION':
815 case 'DECIMAL':
816 case 'DEC':
817 case 'FIXED':
818 default:
819 return 'N';