1
0

adodb-mysqli.inc.php 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292
  1. <?php
  2. /*
  3. @version v5.20.3 01-Jan-2016
  4. @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  5. @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
  6. Released under both BSD license and Lesser GPL library license.
  7. Whenever there is any discrepancy between the two licenses,
  8. the BSD license will take precedence.
  9. Set tabs to 8.
  10. This is the preferred driver for MySQL connections, and supports both transactional
  11. and non-transactional table types. You can use this as a drop-in replacement for both
  12. the mysql and mysqlt drivers. As of ADOdb Version 5.20.0, all other native MySQL drivers
  13. are deprecated
  14. Requires mysql client. Works on Windows and Unix.
  15. 21 October 2003: MySQLi extension implementation by Arjen de Rijke (a.de.rijke@xs4all.nl)
  16. Based on adodb 3.40
  17. */
  18. // security - hide paths
  19. if (!defined('ADODB_DIR')) die();
  20. if (! defined("_ADODB_MYSQLI_LAYER")) {
  21. define("_ADODB_MYSQLI_LAYER", 1 );
  22. // PHP5 compat...
  23. if (! defined("MYSQLI_BINARY_FLAG")) define("MYSQLI_BINARY_FLAG", 128);
  24. if (!defined('MYSQLI_READ_DEFAULT_GROUP')) define('MYSQLI_READ_DEFAULT_GROUP',1);
  25. // disable adodb extension - currently incompatible.
  26. global $ADODB_EXTENSION; $ADODB_EXTENSION = false;
  27. class ADODB_mysqli extends ADOConnection {
  28. var $databaseType = 'mysqli';
  29. var $dataProvider = 'mysql';
  30. var $hasInsertID = true;
  31. var $hasAffectedRows = true;
  32. var $metaTablesSQL = "SELECT
  33. TABLE_NAME,
  34. CASE WHEN TABLE_TYPE = 'VIEW' THEN 'V' ELSE 'T' END
  35. FROM INFORMATION_SCHEMA.TABLES
  36. WHERE TABLE_SCHEMA=";
  37. var $metaColumnsSQL = "SHOW COLUMNS FROM `%s`";
  38. var $fmtTimeStamp = "'Y-m-d H:i:s'";
  39. var $hasLimit = true;
  40. var $hasMoveFirst = true;
  41. var $hasGenID = true;
  42. var $isoDates = true; // accepts dates in ISO format
  43. var $sysDate = 'CURDATE()';
  44. var $sysTimeStamp = 'NOW()';
  45. var $hasTransactions = true;
  46. var $forceNewConnect = false;
  47. var $poorAffectedRows = true;
  48. var $clientFlags = 0;
  49. var $substr = "substring";
  50. var $port = 3306; //Default to 3306 to fix HHVM bug
  51. var $socket = ''; //Default to empty string to fix HHVM bug
  52. var $_bindInputArray = false;
  53. var $nameQuote = '`'; /// string to use to quote identifiers and names
  54. var $optionFlags = array(array(MYSQLI_READ_DEFAULT_GROUP,0));
  55. var $arrayClass = 'ADORecordSet_array_mysqli';
  56. var $multiQuery = false;
  57. function __construct()
  58. {
  59. // if(!extension_loaded("mysqli"))
  60. //trigger_error("You must have the mysqli extension installed.", E_USER_ERROR);
  61. }
  62. function SetTransactionMode( $transaction_mode )
  63. {
  64. $this->_transmode = $transaction_mode;
  65. if (empty($transaction_mode)) {
  66. $this->Execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ');
  67. return;
  68. }
  69. if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
  70. $this->Execute("SET SESSION TRANSACTION ".$transaction_mode);
  71. }
  72. // returns true or false
  73. // To add: parameter int $port,
  74. // parameter string $socket
  75. function _connect($argHostname = NULL,
  76. $argUsername = NULL,
  77. $argPassword = NULL,
  78. $argDatabasename = NULL, $persist=false)
  79. {
  80. if(!extension_loaded("mysqli")) {
  81. return null;
  82. }
  83. $this->_connectionID = @mysqli_init();
  84. if (is_null($this->_connectionID)) {
  85. // mysqli_init only fails if insufficient memory
  86. if ($this->debug) {
  87. ADOConnection::outp("mysqli_init() failed : " . $this->ErrorMsg());
  88. }
  89. return false;
  90. }
  91. /*
  92. I suggest a simple fix which would enable adodb and mysqli driver to
  93. read connection options from the standard mysql configuration file
  94. /etc/my.cnf - "Bastien Duclaux" <bduclaux#yahoo.com>
  95. */
  96. foreach($this->optionFlags as $arr) {
  97. mysqli_options($this->_connectionID,$arr[0],$arr[1]);
  98. }
  99. //http ://php.net/manual/en/mysqli.persistconns.php
  100. if ($persist && PHP_VERSION > 5.2 && strncmp($argHostname,'p:',2) != 0) $argHostname = 'p:'.$argHostname;
  101. #if (!empty($this->port)) $argHostname .= ":".$this->port;
  102. $ok = mysqli_real_connect($this->_connectionID,
  103. $argHostname,
  104. $argUsername,
  105. $argPassword,
  106. $argDatabasename,
  107. $this->port,
  108. $this->socket,
  109. $this->clientFlags);
  110. if ($ok) {
  111. if ($argDatabasename) return $this->SelectDB($argDatabasename);
  112. return true;
  113. } else {
  114. if ($this->debug) {
  115. ADOConnection::outp("Could't connect : " . $this->ErrorMsg());
  116. }
  117. $this->_connectionID = null;
  118. return false;
  119. }
  120. }
  121. // returns true or false
  122. // How to force a persistent connection
  123. function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
  124. {
  125. return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
  126. }
  127. // When is this used? Close old connection first?
  128. // In _connect(), check $this->forceNewConnect?
  129. function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
  130. {
  131. $this->forceNewConnect = true;
  132. return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
  133. }
  134. function IfNull( $field, $ifNull )
  135. {
  136. return " IFNULL($field, $ifNull) "; // if MySQL
  137. }
  138. // do not use $ADODB_COUNTRECS
  139. function GetOne($sql,$inputarr=false)
  140. {
  141. global $ADODB_GETONE_EOF;
  142. $ret = false;
  143. $rs = $this->Execute($sql,$inputarr);
  144. if ($rs) {
  145. if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
  146. else $ret = reset($rs->fields);
  147. $rs->Close();
  148. }
  149. return $ret;
  150. }
  151. function ServerInfo()
  152. {
  153. $arr['description'] = $this->GetOne("select version()");
  154. $arr['version'] = ADOConnection::_findvers($arr['description']);
  155. return $arr;
  156. }
  157. function BeginTrans()
  158. {
  159. if ($this->transOff) return true;
  160. $this->transCnt += 1;
  161. //$this->Execute('SET AUTOCOMMIT=0');
  162. mysqli_autocommit($this->_connectionID, false);
  163. $this->Execute('BEGIN');
  164. return true;
  165. }
  166. function CommitTrans($ok=true)
  167. {
  168. if ($this->transOff) return true;
  169. if (!$ok) return $this->RollbackTrans();
  170. if ($this->transCnt) $this->transCnt -= 1;
  171. $this->Execute('COMMIT');
  172. //$this->Execute('SET AUTOCOMMIT=1');
  173. mysqli_autocommit($this->_connectionID, true);
  174. return true;
  175. }
  176. function RollbackTrans()
  177. {
  178. if ($this->transOff) return true;
  179. if ($this->transCnt) $this->transCnt -= 1;
  180. $this->Execute('ROLLBACK');
  181. //$this->Execute('SET AUTOCOMMIT=1');
  182. mysqli_autocommit($this->_connectionID, true);
  183. return true;
  184. }
  185. function RowLock($tables,$where='',$col='1 as adodbignore')
  186. {
  187. if ($this->transCnt==0) $this->BeginTrans();
  188. if ($where) $where = ' where '.$where;
  189. $rs = $this->Execute("select $col from $tables $where for update");
  190. return !empty($rs);
  191. }
  192. /**
  193. * Quotes a string to be sent to the database
  194. * When there is no active connection,
  195. * @param string $s The string to quote
  196. * @param boolean $magic_quotes If false, use mysqli_real_escape_string()
  197. * if you are quoting a string extracted from a POST/GET variable,
  198. * then pass get_magic_quotes_gpc() as the second parameter. This will
  199. * ensure that the variable is not quoted twice, once by qstr() and
  200. * once by the magic_quotes_gpc.
  201. * Eg. $s = $db->qstr(_GET['name'],get_magic_quotes_gpc());
  202. * @return string Quoted string
  203. */
  204. function qstr($s, $magic_quotes = false)
  205. {
  206. if (is_null($s)) return 'NULL';
  207. if (!$magic_quotes) {
  208. // mysqli_real_escape_string() throws a warning when the given
  209. // connection is invalid
  210. if (PHP_VERSION >= 5 && $this->_connectionID) {
  211. return "'" . mysqli_real_escape_string($this->_connectionID, $s) . "'";
  212. }
  213. if ($this->replaceQuote[0] == '\\') {
  214. $s = adodb_str_replace(array('\\',"\0"), array('\\\\',"\\\0") ,$s);
  215. }
  216. return "'" . str_replace("'", $this->replaceQuote, $s) . "'";
  217. }
  218. // undo magic quotes for "
  219. $s = str_replace('\\"','"',$s);
  220. return "'$s'";
  221. }
  222. function _insertid()
  223. {
  224. $result = @mysqli_insert_id($this->_connectionID);
  225. if ($result == -1) {
  226. if ($this->debug) ADOConnection::outp("mysqli_insert_id() failed : " . $this->ErrorMsg());
  227. }
  228. return $result;
  229. }
  230. // Only works for INSERT, UPDATE and DELETE query's
  231. function _affectedrows()
  232. {
  233. $result = @mysqli_affected_rows($this->_connectionID);
  234. if ($result == -1) {
  235. if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : " . $this->ErrorMsg());
  236. }
  237. return $result;
  238. }
  239. // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html
  240. // Reference on Last_Insert_ID on the recommended way to simulate sequences
  241. var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
  242. var $_genSeqSQL = "create table if not exists %s (id int not null)";
  243. var $_genSeqCountSQL = "select count(*) from %s";
  244. var $_genSeq2SQL = "insert into %s values (%s)";
  245. var $_dropSeqSQL = "drop table if exists %s";
  246. function CreateSequence($seqname='adodbseq',$startID=1)
  247. {
  248. if (empty($this->_genSeqSQL)) return false;
  249. $u = strtoupper($seqname);
  250. $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
  251. if (!$ok) return false;
  252. return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
  253. }
  254. function GenID($seqname='adodbseq',$startID=1)
  255. {
  256. // post-nuke sets hasGenID to false
  257. if (!$this->hasGenID) return false;
  258. $getnext = sprintf($this->_genIDSQL,$seqname);
  259. $holdtransOK = $this->_transOK; // save the current status
  260. $rs = @$this->Execute($getnext);
  261. if (!$rs) {
  262. if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
  263. $u = strtoupper($seqname);
  264. $this->Execute(sprintf($this->_genSeqSQL,$seqname));
  265. $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL,$seqname));
  266. if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
  267. $rs = $this->Execute($getnext);
  268. }
  269. if ($rs) {
  270. $this->genID = mysqli_insert_id($this->_connectionID);
  271. $rs->Close();
  272. } else
  273. $this->genID = 0;
  274. return $this->genID;
  275. }
  276. function MetaDatabases()
  277. {
  278. $query = "SHOW DATABASES";
  279. $ret = $this->Execute($query);
  280. if ($ret && is_object($ret)){
  281. $arr = array();
  282. while (!$ret->EOF){
  283. $db = $ret->Fields('Database');
  284. if ($db != 'mysql') $arr[] = $db;
  285. $ret->MoveNext();
  286. }
  287. return $arr;
  288. }
  289. return $ret;
  290. }
  291. function MetaIndexes ($table, $primary = FALSE, $owner = false)
  292. {
  293. // save old fetch mode
  294. global $ADODB_FETCH_MODE;
  295. $false = false;
  296. $save = $ADODB_FETCH_MODE;
  297. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  298. if ($this->fetchMode !== FALSE) {
  299. $savem = $this->SetFetchMode(FALSE);
  300. }
  301. // get index details
  302. $rs = $this->Execute(sprintf('SHOW INDEXES FROM %s',$table));
  303. // restore fetchmode
  304. if (isset($savem)) {
  305. $this->SetFetchMode($savem);
  306. }
  307. $ADODB_FETCH_MODE = $save;
  308. if (!is_object($rs)) {
  309. return $false;
  310. }
  311. $indexes = array ();
  312. // parse index data into array
  313. while ($row = $rs->FetchRow()) {
  314. if ($primary == FALSE AND $row[2] == 'PRIMARY') {
  315. continue;
  316. }
  317. if (!isset($indexes[$row[2]])) {
  318. $indexes[$row[2]] = array(
  319. 'unique' => ($row[1] == 0),
  320. 'columns' => array()
  321. );
  322. }
  323. $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
  324. }
  325. // sort columns by order in the index
  326. foreach ( array_keys ($indexes) as $index )
  327. {
  328. ksort ($indexes[$index]['columns']);
  329. }
  330. return $indexes;
  331. }
  332. // Format date column in sql string given an input format that understands Y M D
  333. function SQLDate($fmt, $col=false)
  334. {
  335. if (!$col) $col = $this->sysTimeStamp;
  336. $s = 'DATE_FORMAT('.$col.",'";
  337. $concat = false;
  338. $len = strlen($fmt);
  339. for ($i=0; $i < $len; $i++) {
  340. $ch = $fmt[$i];
  341. switch($ch) {
  342. case 'Y':
  343. case 'y':
  344. $s .= '%Y';
  345. break;
  346. case 'Q':
  347. case 'q':
  348. $s .= "'),Quarter($col)";
  349. if ($len > $i+1) $s .= ",DATE_FORMAT($col,'";
  350. else $s .= ",('";
  351. $concat = true;
  352. break;
  353. case 'M':
  354. $s .= '%b';
  355. break;
  356. case 'm':
  357. $s .= '%m';
  358. break;
  359. case 'D':
  360. case 'd':
  361. $s .= '%d';
  362. break;
  363. case 'H':
  364. $s .= '%H';
  365. break;
  366. case 'h':
  367. $s .= '%I';
  368. break;
  369. case 'i':
  370. $s .= '%i';
  371. break;
  372. case 's':
  373. $s .= '%s';
  374. break;
  375. case 'a':
  376. case 'A':
  377. $s .= '%p';
  378. break;
  379. case 'w':
  380. $s .= '%w';
  381. break;
  382. case 'l':
  383. $s .= '%W';
  384. break;
  385. default:
  386. if ($ch == '\\') {
  387. $i++;
  388. $ch = substr($fmt,$i,1);
  389. }
  390. $s .= $ch;
  391. break;
  392. }
  393. }
  394. $s.="')";
  395. if ($concat) $s = "CONCAT($s)";
  396. return $s;
  397. }
  398. // returns concatenated string
  399. // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
  400. function Concat()
  401. {
  402. $s = "";
  403. $arr = func_get_args();
  404. // suggestion by andrew005@mnogo.ru
  405. $s = implode(',',$arr);
  406. if (strlen($s) > 0) return "CONCAT($s)";
  407. else return '';
  408. }
  409. // dayFraction is a day in floating point
  410. function OffsetDate($dayFraction,$date=false)
  411. {
  412. if (!$date) $date = $this->sysDate;
  413. $fraction = $dayFraction * 24 * 3600;
  414. return $date . ' + INTERVAL ' . $fraction.' SECOND';
  415. // return "from_unixtime(unix_timestamp($date)+$fraction)";
  416. }
  417. function MetaProcedures($NamePattern = false, $catalog = null, $schemaPattern = null)
  418. {
  419. // save old fetch mode
  420. global $ADODB_FETCH_MODE;
  421. $false = false;
  422. $save = $ADODB_FETCH_MODE;
  423. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  424. if ($this->fetchMode !== FALSE) {
  425. $savem = $this->SetFetchMode(FALSE);
  426. }
  427. $procedures = array ();
  428. // get index details
  429. $likepattern = '';
  430. if ($NamePattern) {
  431. $likepattern = " LIKE '".$NamePattern."'";
  432. }
  433. $rs = $this->Execute('SHOW PROCEDURE STATUS'.$likepattern);
  434. if (is_object($rs)) {
  435. // parse index data into array
  436. while ($row = $rs->FetchRow()) {
  437. $procedures[$row[1]] = array(
  438. 'type' => 'PROCEDURE',
  439. 'catalog' => '',
  440. 'schema' => '',
  441. 'remarks' => $row[7],
  442. );
  443. }
  444. }
  445. $rs = $this->Execute('SHOW FUNCTION STATUS'.$likepattern);
  446. if (is_object($rs)) {
  447. // parse index data into array
  448. while ($row = $rs->FetchRow()) {
  449. $procedures[$row[1]] = array(
  450. 'type' => 'FUNCTION',
  451. 'catalog' => '',
  452. 'schema' => '',
  453. 'remarks' => $row[7]
  454. );
  455. }
  456. }
  457. // restore fetchmode
  458. if (isset($savem)) {
  459. $this->SetFetchMode($savem);
  460. }
  461. $ADODB_FETCH_MODE = $save;
  462. return $procedures;
  463. }
  464. /**
  465. * Retrieves a list of tables based on given criteria
  466. *
  467. * @param string $ttype Table type = 'TABLE', 'VIEW' or false=both (default)
  468. * @param string $showSchema schema name, false = current schema (default)
  469. * @param string $mask filters the table by name
  470. *
  471. * @return array list of tables
  472. */
  473. function MetaTables($ttype=false,$showSchema=false,$mask=false)
  474. {
  475. $save = $this->metaTablesSQL;
  476. if ($showSchema && is_string($showSchema)) {
  477. $this->metaTablesSQL .= $this->qstr($showSchema);
  478. } else {
  479. $this->metaTablesSQL .= "schema()";
  480. }
  481. if ($mask) {
  482. $mask = $this->qstr($mask);
  483. $this->metaTablesSQL .= " AND table_name LIKE $mask";
  484. }
  485. $ret = ADOConnection::MetaTables($ttype,$showSchema);
  486. $this->metaTablesSQL = $save;
  487. return $ret;
  488. }
  489. // "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx>
  490. function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE )
  491. {
  492. global $ADODB_FETCH_MODE;
  493. if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true;
  494. if ( !empty($owner) ) {
  495. $table = "$owner.$table";
  496. }
  497. $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
  498. if ($associative) {
  499. $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"];
  500. } else $create_sql = $a_create_table[1];
  501. $matches = array();
  502. if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
  503. $foreign_keys = array();
  504. $num_keys = count($matches[0]);
  505. for ( $i = 0; $i < $num_keys; $i ++ ) {
  506. $my_field = explode('`, `', $matches[1][$i]);
  507. $ref_table = $matches[2][$i];
  508. $ref_field = explode('`, `', $matches[3][$i]);
  509. if ( $upper ) {
  510. $ref_table = strtoupper($ref_table);
  511. }
  512. // see https://sourceforge.net/tracker/index.php?func=detail&aid=2287278&group_id=42718&atid=433976
  513. if (!isset($foreign_keys[$ref_table])) {
  514. $foreign_keys[$ref_table] = array();
  515. }
  516. $num_fields = count($my_field);
  517. for ( $j = 0; $j < $num_fields; $j ++ ) {
  518. if ( $associative ) {
  519. $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
  520. } else {
  521. $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
  522. }
  523. }
  524. }
  525. return $foreign_keys;
  526. }
  527. function MetaColumns($table, $normalize=true)
  528. {
  529. $false = false;
  530. if (!$this->metaColumnsSQL)
  531. return $false;
  532. global $ADODB_FETCH_MODE;
  533. $save = $ADODB_FETCH_MODE;
  534. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  535. if ($this->fetchMode !== false)
  536. $savem = $this->SetFetchMode(false);
  537. $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
  538. if (isset($savem)) $this->SetFetchMode($savem);
  539. $ADODB_FETCH_MODE = $save;
  540. if (!is_object($rs))
  541. return $false;
  542. $retarr = array();
  543. while (!$rs->EOF) {
  544. $fld = new ADOFieldObject();
  545. $fld->name = $rs->fields[0];
  546. $type = $rs->fields[1];
  547. // split type into type(length):
  548. $fld->scale = null;
  549. if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
  550. $fld->type = $query_array[1];
  551. $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
  552. $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
  553. } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
  554. $fld->type = $query_array[1];
  555. $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
  556. } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
  557. $fld->type = $query_array[1];
  558. $arr = explode(",",$query_array[2]);
  559. $fld->enums = $arr;
  560. $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
  561. $fld->max_length = ($zlen > 0) ? $zlen : 1;
  562. } else {
  563. $fld->type = $type;
  564. $fld->max_length = -1;
  565. }
  566. $fld->not_null = ($rs->fields[2] != 'YES');
  567. $fld->primary_key = ($rs->fields[3] == 'PRI');
  568. $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
  569. $fld->binary = (strpos($type,'blob') !== false);
  570. $fld->unsigned = (strpos($type,'unsigned') !== false);
  571. $fld->zerofill = (strpos($type,'zerofill') !== false);
  572. if (!$fld->binary) {
  573. $d = $rs->fields[4];
  574. if ($d != '' && $d != 'NULL') {
  575. $fld->has_default = true;
  576. $fld->default_value = $d;
  577. } else {
  578. $fld->has_default = false;
  579. }
  580. }
  581. if ($save == ADODB_FETCH_NUM) {
  582. $retarr[] = $fld;
  583. } else {
  584. $retarr[strtoupper($fld->name)] = $fld;
  585. }
  586. $rs->MoveNext();
  587. }
  588. $rs->Close();
  589. return $retarr;
  590. }
  591. // returns true or false
  592. function SelectDB($dbName)
  593. {
  594. // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
  595. $this->database = $dbName;
  596. $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
  597. if ($this->_connectionID) {
  598. $result = @mysqli_select_db($this->_connectionID, $dbName);
  599. if (!$result) {
  600. ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg());
  601. }
  602. return $result;
  603. }
  604. return false;
  605. }
  606. // parameters use PostgreSQL convention, not MySQL
  607. function SelectLimit($sql,
  608. $nrows = -1,
  609. $offset = -1,
  610. $inputarr = false,
  611. $secs = 0)
  612. {
  613. $offsetStr = ($offset >= 0) ? "$offset," : '';
  614. if ($nrows < 0) $nrows = '18446744073709551615';
  615. if ($secs)
  616. $rs = $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr );
  617. else
  618. $rs = $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr );
  619. return $rs;
  620. }
  621. function Prepare($sql)
  622. {
  623. return $sql;
  624. $stmt = $this->_connectionID->prepare($sql);
  625. if (!$stmt) {
  626. echo $this->ErrorMsg();
  627. return $sql;
  628. }
  629. return array($sql,$stmt);
  630. }
  631. // returns queryID or false
  632. function _query($sql, $inputarr)
  633. {
  634. global $ADODB_COUNTRECS;
  635. // Move to the next recordset, or return false if there is none. In a stored proc
  636. // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
  637. // returns false. I think this is because the last "recordset" is actually just the
  638. // return value of the stored proc (ie the number of rows affected).
  639. // Commented out for reasons of performance. You should retrieve every recordset yourself.
  640. // if (!mysqli_next_result($this->connection->_connectionID)) return false;
  641. if (is_array($sql)) {
  642. // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but
  643. // returns as bound variables.
  644. $stmt = $sql[1];
  645. $a = '';
  646. foreach($inputarr as $k => $v) {
  647. if (is_string($v)) $a .= 's';
  648. else if (is_integer($v)) $a .= 'i';
  649. else $a .= 'd';
  650. }
  651. $fnarr = array_merge( array($stmt,$a) , $inputarr);
  652. $ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr);
  653. $ret = mysqli_stmt_execute($stmt);
  654. return $ret;
  655. }
  656. /*
  657. if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
  658. if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
  659. return false;
  660. }
  661. return $mysql_res;
  662. */
  663. if ($this->multiQuery) {
  664. $rs = mysqli_multi_query($this->_connectionID, $sql.';');
  665. if ($rs) {
  666. $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID );
  667. return $rs ? $rs : true; // mysqli_more_results( $this->_connectionID )
  668. }
  669. } else {
  670. $rs = mysqli_query($this->_connectionID, $sql, $ADODB_COUNTRECS ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
  671. if ($rs) return $rs;
  672. }
  673. if($this->debug)
  674. ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
  675. return false;
  676. }
  677. /* Returns: the last error message from previous database operation */
  678. function ErrorMsg()
  679. {
  680. if (empty($this->_connectionID))
  681. $this->_errorMsg = @mysqli_connect_error();
  682. else
  683. $this->_errorMsg = @mysqli_error($this->_connectionID);
  684. return $this->_errorMsg;
  685. }
  686. /* Returns: the last error number from previous database operation */
  687. function ErrorNo()
  688. {
  689. if (empty($this->_connectionID))
  690. return @mysqli_connect_errno();
  691. else
  692. return @mysqli_errno($this->_connectionID);
  693. }
  694. // returns true or false
  695. function _close()
  696. {
  697. @mysqli_close($this->_connectionID);
  698. $this->_connectionID = false;
  699. }
  700. /*
  701. * Maximum size of C field
  702. */
  703. function CharMax()
  704. {
  705. return 255;
  706. }
  707. /*
  708. * Maximum size of X field
  709. */
  710. function TextMax()
  711. {
  712. return 4294967295;
  713. }
  714. // this is a set of functions for managing client encoding - very important if the encodings
  715. // of your database and your output target (i.e. HTML) don't match
  716. // for instance, you may have UTF8 database and server it on-site as latin1 etc.
  717. // GetCharSet - get the name of the character set the client is using now
  718. // Under Windows, the functions should work with MySQL 4.1.11 and above, the set of charsets supported
  719. // depends on compile flags of mysql distribution
  720. function GetCharSet()
  721. {
  722. //we will use ADO's builtin property charSet
  723. if (!method_exists($this->_connectionID,'character_set_name'))
  724. return false;
  725. $this->charSet = @$this->_connectionID->character_set_name();
  726. if (!$this->charSet) {
  727. return false;
  728. } else {
  729. return $this->charSet;
  730. }
  731. }
  732. // SetCharSet - switch the client encoding
  733. function SetCharSet($charset_name)
  734. {
  735. if (!method_exists($this->_connectionID,'set_charset')) {
  736. return false;
  737. }
  738. if ($this->charSet !== $charset_name) {
  739. $if = @$this->_connectionID->set_charset($charset_name);
  740. return ($if === true & $this->GetCharSet() == $charset_name);
  741. } else {
  742. return true;
  743. }
  744. }
  745. }
  746. /*--------------------------------------------------------------------------------------
  747. Class Name: Recordset
  748. --------------------------------------------------------------------------------------*/
  749. class ADORecordSet_mysqli extends ADORecordSet{
  750. var $databaseType = "mysqli";
  751. var $canSeek = true;
  752. function __construct($queryID, $mode = false)
  753. {
  754. if ($mode === false) {
  755. global $ADODB_FETCH_MODE;
  756. $mode = $ADODB_FETCH_MODE;
  757. }
  758. switch ($mode) {
  759. case ADODB_FETCH_NUM:
  760. $this->fetchMode = MYSQLI_NUM;
  761. break;
  762. case ADODB_FETCH_ASSOC:
  763. $this->fetchMode = MYSQLI_ASSOC;
  764. break;
  765. case ADODB_FETCH_DEFAULT:
  766. case ADODB_FETCH_BOTH:
  767. default:
  768. $this->fetchMode = MYSQLI_BOTH;
  769. break;
  770. }
  771. $this->adodbFetchMode = $mode;
  772. parent::__construct($queryID);
  773. }
  774. function _initrs()
  775. {
  776. global $ADODB_COUNTRECS;
  777. $this->_numOfRows = $ADODB_COUNTRECS ? @mysqli_num_rows($this->_queryID) : -1;
  778. $this->_numOfFields = @mysqli_num_fields($this->_queryID);
  779. }
  780. /*
  781. 1 = MYSQLI_NOT_NULL_FLAG
  782. 2 = MYSQLI_PRI_KEY_FLAG
  783. 4 = MYSQLI_UNIQUE_KEY_FLAG
  784. 8 = MYSQLI_MULTIPLE_KEY_FLAG
  785. 16 = MYSQLI_BLOB_FLAG
  786. 32 = MYSQLI_UNSIGNED_FLAG
  787. 64 = MYSQLI_ZEROFILL_FLAG
  788. 128 = MYSQLI_BINARY_FLAG
  789. 256 = MYSQLI_ENUM_FLAG
  790. 512 = MYSQLI_AUTO_INCREMENT_FLAG
  791. 1024 = MYSQLI_TIMESTAMP_FLAG
  792. 2048 = MYSQLI_SET_FLAG
  793. 32768 = MYSQLI_NUM_FLAG
  794. 16384 = MYSQLI_PART_KEY_FLAG
  795. 32768 = MYSQLI_GROUP_FLAG
  796. 65536 = MYSQLI_UNIQUE_FLAG
  797. 131072 = MYSQLI_BINCMP_FLAG
  798. */
  799. function FetchField($fieldOffset = -1)
  800. {
  801. $fieldnr = $fieldOffset;
  802. if ($fieldOffset != -1) {
  803. $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr);
  804. }
  805. $o = @mysqli_fetch_field($this->_queryID);
  806. if (!$o) return false;
  807. //Fix for HHVM
  808. if ( !isset($o->flags) ) {
  809. $o->flags = 0;
  810. }
  811. /* Properties of an ADOFieldObject as set by MetaColumns */
  812. $o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG;
  813. $o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG;
  814. $o->auto_increment = $o->flags & MYSQLI_AUTO_INCREMENT_FLAG;
  815. $o->binary = $o->flags & MYSQLI_BINARY_FLAG;
  816. // $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */
  817. $o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG;
  818. return $o;
  819. }
  820. function GetRowAssoc($upper = ADODB_ASSOC_CASE)
  821. {
  822. if ($this->fetchMode == MYSQLI_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) {
  823. return $this->fields;
  824. }
  825. $row = ADORecordSet::GetRowAssoc($upper);
  826. return $row;
  827. }
  828. /* Use associative array to get fields array */
  829. function Fields($colname)
  830. {
  831. if ($this->fetchMode != MYSQLI_NUM) {
  832. return @$this->fields[$colname];
  833. }
  834. if (!$this->bind) {
  835. $this->bind = array();
  836. for ($i = 0; $i < $this->_numOfFields; $i++) {
  837. $o = $this->FetchField($i);
  838. $this->bind[strtoupper($o->name)] = $i;
  839. }
  840. }
  841. return $this->fields[$this->bind[strtoupper($colname)]];
  842. }
  843. function _seek($row)
  844. {
  845. if ($this->_numOfRows == 0 || $row < 0) {
  846. return false;
  847. }
  848. mysqli_data_seek($this->_queryID, $row);
  849. $this->EOF = false;
  850. return true;
  851. }
  852. function NextRecordSet()
  853. {
  854. global $ADODB_COUNTRECS;
  855. mysqli_free_result($this->_queryID);
  856. $this->_queryID = -1;
  857. // Move to the next recordset, or return false if there is none. In a stored proc
  858. // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
  859. // returns false. I think this is because the last "recordset" is actually just the
  860. // return value of the stored proc (ie the number of rows affected).
  861. if(!mysqli_next_result($this->connection->_connectionID)) {
  862. return false;
  863. }
  864. // CD: There is no $this->_connectionID variable, at least in the ADO version I'm using
  865. $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->connection->_connectionID )
  866. : @mysqli_use_result( $this->connection->_connectionID );
  867. if(!$this->_queryID) {
  868. return false;
  869. }
  870. $this->_inited = false;
  871. $this->bind = false;
  872. $this->_currentRow = -1;
  873. $this->Init();
  874. return true;
  875. }
  876. // 10% speedup to move MoveNext to child class
  877. // This is the only implementation that works now (23-10-2003).
  878. // Other functions return no or the wrong results.
  879. function MoveNext()
  880. {
  881. if ($this->EOF) return false;
  882. $this->_currentRow++;
  883. $this->fields = @mysqli_fetch_array($this->_queryID,$this->fetchMode);
  884. if (is_array($this->fields)) {
  885. $this->_updatefields();
  886. return true;
  887. }
  888. $this->EOF = true;
  889. return false;
  890. }
  891. function _fetch()
  892. {
  893. $this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode);
  894. $this->_updatefields();
  895. return is_array($this->fields);
  896. }
  897. function _close()
  898. {
  899. //if results are attached to this pointer from Stored Proceedure calls, the next standard query will die 2014
  900. //only a problem with persistant connections
  901. if($this->connection->_connectionID) {
  902. while(mysqli_more_results($this->connection->_connectionID)){
  903. mysqli_next_result($this->connection->_connectionID);
  904. }
  905. }
  906. if($this->_queryID) {
  907. mysqli_free_result($this->_queryID);
  908. }
  909. $this->_queryID = false;
  910. }
  911. /*
  912. 0 = MYSQLI_TYPE_DECIMAL
  913. 1 = MYSQLI_TYPE_CHAR
  914. 1 = MYSQLI_TYPE_TINY
  915. 2 = MYSQLI_TYPE_SHORT
  916. 3 = MYSQLI_TYPE_LONG
  917. 4 = MYSQLI_TYPE_FLOAT
  918. 5 = MYSQLI_TYPE_DOUBLE
  919. 6 = MYSQLI_TYPE_NULL
  920. 7 = MYSQLI_TYPE_TIMESTAMP
  921. 8 = MYSQLI_TYPE_LONGLONG
  922. 9 = MYSQLI_TYPE_INT24
  923. 10 = MYSQLI_TYPE_DATE
  924. 11 = MYSQLI_TYPE_TIME
  925. 12 = MYSQLI_TYPE_DATETIME
  926. 13 = MYSQLI_TYPE_YEAR
  927. 14 = MYSQLI_TYPE_NEWDATE
  928. 247 = MYSQLI_TYPE_ENUM
  929. 248 = MYSQLI_TYPE_SET
  930. 249 = MYSQLI_TYPE_TINY_BLOB
  931. 250 = MYSQLI_TYPE_MEDIUM_BLOB
  932. 251 = MYSQLI_TYPE_LONG_BLOB
  933. 252 = MYSQLI_TYPE_BLOB
  934. 253 = MYSQLI_TYPE_VAR_STRING
  935. 254 = MYSQLI_TYPE_STRING
  936. 255 = MYSQLI_TYPE_GEOMETRY
  937. */
  938. function MetaType($t, $len = -1, $fieldobj = false)
  939. {
  940. if (is_object($t)) {
  941. $fieldobj = $t;
  942. $t = $fieldobj->type;
  943. $len = $fieldobj->max_length;
  944. }
  945. $len = -1; // mysql max_length is not accurate
  946. switch (strtoupper($t)) {
  947. case 'STRING':
  948. case 'CHAR':
  949. case 'VARCHAR':
  950. case 'TINYBLOB':
  951. case 'TINYTEXT':
  952. case 'ENUM':
  953. case 'SET':
  954. case MYSQLI_TYPE_TINY_BLOB :
  955. #case MYSQLI_TYPE_CHAR :
  956. case MYSQLI_TYPE_STRING :
  957. case MYSQLI_TYPE_ENUM :
  958. case MYSQLI_TYPE_SET :
  959. case 253 :
  960. if ($len <= $this->blobSize) return 'C';
  961. case 'TEXT':
  962. case 'LONGTEXT':
  963. case 'MEDIUMTEXT':
  964. return 'X';
  965. // php_mysql extension always returns 'blob' even if 'text'
  966. // so we have to check whether binary...
  967. case 'IMAGE':
  968. case 'LONGBLOB':
  969. case 'BLOB':
  970. case 'MEDIUMBLOB':
  971. case MYSQLI_TYPE_BLOB :
  972. case MYSQLI_TYPE_LONG_BLOB :
  973. case MYSQLI_TYPE_MEDIUM_BLOB :
  974. return !empty($fieldobj->binary) ? 'B' : 'X';
  975. case 'YEAR':
  976. case 'DATE':
  977. case MYSQLI_TYPE_DATE :
  978. case MYSQLI_TYPE_YEAR :
  979. return 'D';
  980. case 'TIME':
  981. case 'DATETIME':
  982. case 'TIMESTAMP':
  983. case MYSQLI_TYPE_DATETIME :
  984. case MYSQLI_TYPE_NEWDATE :
  985. case MYSQLI_TYPE_TIME :
  986. case MYSQLI_TYPE_TIMESTAMP :
  987. return 'T';
  988. case 'INT':
  989. case 'INTEGER':
  990. case 'BIGINT':
  991. case 'TINYINT':
  992. case 'MEDIUMINT':
  993. case 'SMALLINT':
  994. case MYSQLI_TYPE_INT24 :
  995. case MYSQLI_TYPE_LONG :
  996. case MYSQLI_TYPE_LONGLONG :
  997. case MYSQLI_TYPE_SHORT :
  998. case MYSQLI_TYPE_TINY :
  999. if (!empty($fieldobj->primary_key)) return 'R';
  1000. return 'I';
  1001. // Added floating-point types
  1002. // Maybe not necessery.
  1003. case 'FLOAT':
  1004. case 'DOUBLE':
  1005. // case 'DOUBLE PRECISION':
  1006. case 'DECIMAL':
  1007. case 'DEC':
  1008. case 'FIXED':
  1009. default:
  1010. //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
  1011. return 'N';
  1012. }
  1013. } // function
  1014. } // rs class
  1015. }
  1016. class ADORecordSet_array_mysqli extends ADORecordSet_array {
  1017. function __construct($id=-1,$mode=false)
  1018. {
  1019. parent::__construct($id,$mode);
  1020. }
  1021. function MetaType($t, $len = -1, $fieldobj = false)
  1022. {
  1023. if (is_object($t)) {
  1024. $fieldobj = $t;
  1025. $t = $fieldobj->type;
  1026. $len = $fieldobj->max_length;
  1027. }
  1028. $len = -1; // mysql max_length is not accurate
  1029. switch (strtoupper($t)) {
  1030. case 'STRING':
  1031. case 'CHAR':
  1032. case 'VARCHAR':
  1033. case 'TINYBLOB':
  1034. case 'TINYTEXT':
  1035. case 'ENUM':
  1036. case 'SET':
  1037. case MYSQLI_TYPE_TINY_BLOB :
  1038. #case MYSQLI_TYPE_CHAR :
  1039. case MYSQLI_TYPE_STRING :
  1040. case MYSQLI_TYPE_ENUM :
  1041. case MYSQLI_TYPE_SET :
  1042. case 253 :
  1043. if ($len <= $this->blobSize) return 'C';
  1044. case 'TEXT':
  1045. case 'LONGTEXT':
  1046. case 'MEDIUMTEXT':
  1047. return 'X';
  1048. // php_mysql extension always returns 'blob' even if 'text'
  1049. // so we have to check whether binary...
  1050. case 'IMAGE':
  1051. case 'LONGBLOB':
  1052. case 'BLOB':
  1053. case 'MEDIUMBLOB':
  1054. case MYSQLI_TYPE_BLOB :
  1055. case MYSQLI_TYPE_LONG_BLOB :
  1056. case MYSQLI_TYPE_MEDIUM_BLOB :
  1057. return !empty($fieldobj->binary) ? 'B' : 'X';
  1058. case 'YEAR':
  1059. case 'DATE':
  1060. case MYSQLI_TYPE_DATE :
  1061. case MYSQLI_TYPE_YEAR :
  1062. return 'D';
  1063. case 'TIME':
  1064. case 'DATETIME':
  1065. case 'TIMESTAMP':
  1066. case MYSQLI_TYPE_DATETIME :
  1067. case MYSQLI_TYPE_NEWDATE :
  1068. case MYSQLI_TYPE_TIME :
  1069. case MYSQLI_TYPE_TIMESTAMP :
  1070. return 'T';
  1071. case 'INT':
  1072. case 'INTEGER':
  1073. case 'BIGINT':
  1074. case 'TINYINT':
  1075. case 'MEDIUMINT':
  1076. case 'SMALLINT':
  1077. case MYSQLI_TYPE_INT24 :
  1078. case MYSQLI_TYPE_LONG :
  1079. case MYSQLI_TYPE_LONGLONG :
  1080. case MYSQLI_TYPE_SHORT :
  1081. case MYSQLI_TYPE_TINY :
  1082. if (!empty($fieldobj->primary_key)) return 'R';
  1083. return 'I';
  1084. // Added floating-point types
  1085. // Maybe not necessery.
  1086. case 'FLOAT':
  1087. case 'DOUBLE':
  1088. // case 'DOUBLE PRECISION':
  1089. case 'DECIMAL':
  1090. case 'DEC':
  1091. case 'FIXED':
  1092. default:
  1093. //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
  1094. return 'N';
  1095. }
  1096. } // function
  1097. }