diff options
122 files changed, 9337 insertions, 3824 deletions
diff --git a/.gitattributes b/.gitattributes index 2cb098d8..083fe55d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,3 +15,10 @@ # Binary files *.gif binary +# Exclude files and dirs from git archive +# matching what buildrelease.py does +.git* export-ignore +.mailmap export-ignore +replicate/ export-ignore +scripts/ export-ignore +tests/ export-ignore @@ -1,4 +1,4 @@ Andreas Fernandez <a.fernandez@scripting-base.de> <andreas.fernandez@aspedia.de> Mike Benoit <mikeb@timetrex.com> MikeB <ipso@snappymail.ca> Mike Benoit <mikeb@timetrex.com> mike.benoit - +Marcel Ouellette <marcel.ouellette@gmail.com> @@ -1,4 +1,4 @@ -ADOdb Library for PHP5 +ADOdb Library for PHP ====================== [](https://gitter.im/adodb/adodb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -27,8 +27,8 @@ need for a database class library to hide the differences between the different databases (encapsulate the differences) so we can easily switch databases. -The library currently supports MySQL, Interbase, Sybase, PostgreSQL, Oracle, -Microsoft SQL server, Foxpro ODBC, Access ODBC, Informix, DB2, +The library currently supports MySQL, Firebird & Interbase, PostgreSQL, SQLite3, Oracle, +Microsoft SQL Server, Foxpro ODBC, Access ODBC, Informix, DB2, Sybase, Sybase SQL Anywhere, generic ODBC and Microsoft's ADO. We hope more people will contribute drivers to support other databases. @@ -48,12 +48,12 @@ You can debug using: <?php include('adodb/adodb.inc.php'); -$db = ADONewConnection($driver); # eg. 'mysql' or 'oci8' +$db = adoNewConnection($driver); # eg. 'mysqli' or 'oci8' $db->debug = true; -$db->Connect($server, $user, $password, $database); -$rs = $db->Execute('select * from some_small_table'); +$db->connect($server, $user, $password, $database); +$rs = $db->execute('select * from some_small_table'); print "<pre>"; -print_r($rs->GetRows()); +print_r($rs->getRows()); print "</pre>"; ``` diff --git a/adodb-active-record.inc.php b/adodb-active-record.inc.php index 4caae158..3b6b5468 100644 --- a/adodb-active-record.inc.php +++ b/adodb-active-record.inc.php @@ -52,34 +52,31 @@ function ADODB_SetDatabaseAdapter(&$db, $index=false) { global $_ADODB_ACTIVE_DBS; - foreach($_ADODB_ACTIVE_DBS as $k => $d) { - if (PHP_VERSION >= 5) { - if ($d->db === $db) { - return $k; - } - } else { - if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) { - return $k; - } - } + foreach($_ADODB_ACTIVE_DBS as $k => $d) { + if($d->db === $db) { + return $k; } + } - $obj = new ADODB_Active_DB(); - $obj->db = $db; - $obj->tables = array(); + $obj = new ADODB_Active_DB(); + $obj->db = $db; + $obj->tables = array(); - if ($index == false) { - $index = sizeof($_ADODB_ACTIVE_DBS); - } + if ($index == false) { + $index = sizeof($_ADODB_ACTIVE_DBS); + } - $_ADODB_ACTIVE_DBS[$index] = $obj; + $_ADODB_ACTIVE_DBS[$index] = $obj; - return sizeof($_ADODB_ACTIVE_DBS)-1; + return sizeof($_ADODB_ACTIVE_DBS)-1; } class ADODB_Active_Record { static $_changeNames = true; // dynamically pluralize table names + /* + * Optional parameter that duplicates the ADODB_QUOTE_FIELDNAMES + */ static $_quoteNames = false; static $_foreignSuffix = '_id'; // @@ -499,7 +496,6 @@ class ADODB_Active_Record { break; default: foreach($cols as $name => $fldobj) { - $name = ($fldobj->name); if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { $this->$name = $fldobj->default_value; @@ -522,7 +518,7 @@ class ADODB_Active_Record { $activetab->_created = time(); $s = serialize($activetab); if (!function_exists('adodb_write_file')) { - include(ADODB_DIR.'/adodb-csvlib.inc.php'); + include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); } adodb_write_file($fname,$s); } @@ -700,9 +696,14 @@ class ADODB_Active_Record { $val = false; } - if (is_null($val) || $val === false) { + if (is_null($val) || $val === false) + { + $SQL = sprintf("SELECT MAX(%s) FROM %s", + $this->nameQuoter($db,$fieldname), + $this->nameQuoter($db,$this->_table) + ); // this might not work reliably in multi-user environment - return $db->GetOne("select max(".$fieldname.") from ".$this->_table); + return $db->GetOne($SQL); } return $val; } @@ -749,10 +750,11 @@ class ADODB_Active_Record { foreach($keys as $k) { $f = $table->flds[$k]; if ($f) { - $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); + $columnName = $this->nameQuoter($db,$k); + $parr[] = $columnName.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); } } - return implode(' and ', $parr); + return implode(' AND ', $parr); } @@ -774,7 +776,7 @@ class ADODB_Active_Record { function Load($where=null,$bindarr=false, $lock = false) { - global $ADODB_FETCH_MODE; + global $ADODB_FETCH_MODE; $db = $this->DB(); if (!$db) { @@ -788,7 +790,9 @@ class ADODB_Active_Record { $savem = $db->SetFetchMode(false); } - $qry = "select * from ".$this->_table; + $qry = sprintf("SELECT * FROM %s", + $this->nameQuoter($db,$this->_table) + ); if($where) { $qry .= ' WHERE '.$where; @@ -862,7 +866,7 @@ class ADODB_Active_Record { $val = $this->$name; if(!is_array($val) || !is_null($val) || !array_key_exists($name, $table->keys)) { $valarr[] = $val; - $names[] = $this->_QName($name,$db); + $names[] = $this->nameQuoter($db,$name); $valstr[] = $db->Param($cnt); $cnt += 1; } @@ -871,12 +875,18 @@ class ADODB_Active_Record { if (empty($names)){ foreach($table->flds as $name=>$fld) { $valarr[] = null; - $names[] = $name; + $names[] = $this->nameQuoter($db,$name); $valstr[] = $db->Param($cnt); $cnt += 1; } } - $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')'; + + $tableName = $this->nameQuoter($db,$this->_table); + $sql = sprintf('INSERT INTO %s (%s) VALUES (%s)', + $tableName, + implode(',',$names), + implode(',',$valstr) + ); $ok = $db->Execute($sql,$valarr); if ($ok) { @@ -907,7 +917,14 @@ class ADODB_Active_Record { $table = $this->TableInfo(); $where = $this->GenWhere($db,$table); - $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where; + + $tableName = $this->nameQuoter($db,$this->_table); + + $sql = sprintf('DELETE FROM %s WHERE %s', + $tableName, + $where + ); + $ok = $db->Execute($sql); return $ok ? true : false; @@ -978,8 +995,20 @@ class ADODB_Active_Record { } break; } - - $ok = $db->Replace($this->_table,$arr,$pkey); + + $newArr = array(); + foreach($arr as $k=>$v) + $newArr[$this->nameQuoter($db,$k)] = $v; + $arr = $newArr; + + $newPkey = array(); + foreach($pkey as $k=>$v) + $newPkey[$k] = $this->nameQuoter($db,$v); + $pkey = $newPkey; + + $tableName = $this->nameQuoter($db,$this->_table); + + $ok = $db->Replace($tableName,$arr,$pkey); if ($ok) { $this->_saved = true; // 1= update 2=insert if ($ok == 2) { @@ -1051,7 +1080,7 @@ class ADODB_Active_Record { } $valarr[] = $val; - $pairs[] = $this->_QName($name,$db).'='.$db->Param($cnt); + $pairs[] = $this->nameQuoter($db,$name).'='.$db->Param($cnt); $cnt += 1; } @@ -1060,7 +1089,13 @@ class ADODB_Active_Record { return -1; } - $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where; + $tableName = $this->nameQuoter($db,$this->_table); + + $sql = sprintf('UPDATE %s SET %s WHERE %s', + $tableName, + implode(',',$pairs), + $where); + $ok = $db->Execute($sql,$valarr); if ($ok) { $this->_original = $neworig; @@ -1078,6 +1113,66 @@ class ADODB_Active_Record { return array_keys($table->flds); } + /** + * Quotes the table and column and field names + * + * this honours the ADODB_QUOTE_FIELDNAMES directive. The routines that + * use it should really just call _adodb_getinsertsql and _adodb_getupdatesql + * which is a nice easy project if you are interested + * + * @param obj $db The database connection + * @param string $name The table or column name to quote + * + * @return string The quoted name + */ + final private function nameQuoter($db,$string) + { + global $ADODB_QUOTE_FIELDNAMES; + + if (!$ADODB_QUOTE_FIELDNAMES && !$this->_quoteNames) + /* + * Nothing to be done + */ + return $string; + + if ($this->_quoteNames == 'NONE') + /* + * Force no quoting when ADODB_QUOTE_FIELDNAMES is set + */ + return $string; + + if ($this->_quoteNames) + /* + * Internal setting takes precedence + */ + $quoteMethod = $this->_quoteNames; + + else + $quoteMethod = $ADODB_QUOTE_FIELDNAMES; + + switch ($quoteMethod) + { + case 'LOWER': + $string = strtolower($string); + break; + case 'NATIVE': + /* + * Nothing to be done + */ + break; + case 'UPPER': + default: + $string = strtoupper($string); + } + + $string = sprintf( '%s%s%s', + $db->nameQuote, + $string, + $db->nameQuote + ); + return $string; + } + }; function adodb_GetActiveRecordsClass(&$db, $class, $table,$whereOrderBy,$bindarr, $primkeyArr, @@ -1087,6 +1182,7 @@ global $_ADODB_ACTIVE_DBS; $save = $db->SetFetchMode(ADODB_FETCH_NUM); + $qry = "select * from ".$table; if (!empty($whereOrderBy)) { @@ -1125,7 +1221,7 @@ global $_ADODB_ACTIVE_DBS; // arrRef will be the structure that knows about our objects. // It is an associative array. // We will, however, return arr, preserving regular 0.. order so that - // obj[0] can be used by app developpers. + // obj[0] can be used by app developers. $arrRef = array(); $bTos = array(); // Will store belongTo's indices if any foreach($rows as $row) { diff --git a/adodb-active-recordx.inc.php b/adodb-active-recordx.inc.php index d53c7931..c2f5f03b 100644 --- a/adodb-active-recordx.inc.php +++ b/adodb-active-recordx.inc.php @@ -66,25 +66,19 @@ function ADODB_SetDatabaseAdapter(&$db) { global $_ADODB_ACTIVE_DBS; - foreach($_ADODB_ACTIVE_DBS as $k => $d) { - if (PHP_VERSION >= 5) { - if ($d->db === $db) { - return $k; - } - } else { - if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) { - return $k; - } - } + foreach($_ADODB_ACTIVE_DBS as $k => $d) { + if ($d->db === $db) { + return $k; } + } - $obj = new ADODB_Active_DB(); - $obj->db = $db; - $obj->tables = array(); + $obj = new ADODB_Active_DB(); + $obj->db = $db; + $obj->tables = array(); - $_ADODB_ACTIVE_DBS[] = $obj; + $_ADODB_ACTIVE_DBS[] = $obj; - return sizeof($_ADODB_ACTIVE_DBS)-1; + return sizeof($_ADODB_ACTIVE_DBS)-1; } @@ -551,7 +545,7 @@ class ADODB_Active_Record { $activetab->_created = time(); $s = serialize($activetab); if (!function_exists('adodb_write_file')) { - include(ADODB_DIR.'/adodb-csvlib.inc.php'); + include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); } adodb_write_file($fname,$s); } @@ -1298,7 +1292,7 @@ function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bind if (!isset($_ADODB_ACTIVE_DBS)) { - include(ADODB_DIR.'/adodb-active-record.inc.php'); + include_once(ADODB_DIR.'/adodb-active-record.inc.php'); } if (!class_exists($class)) { $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass'); @@ -1309,7 +1303,7 @@ function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bind // arrRef will be the structure that knows about our objects. // It is an associative array. // We will, however, return arr, preserving regular 0.. order so that - // obj[0] can be used by app developpers. + // obj[0] can be used by app developers. $arrRef = array(); $bTos = array(); // Will store belongTo's indices if any foreach($rows as $row) { diff --git a/adodb-csvlib.inc.php b/adodb-csvlib.inc.php index e7edf1b4..0d0c3613 100644 --- a/adodb-csvlib.inc.php +++ b/adodb-csvlib.inc.php @@ -27,7 +27,7 @@ $ADODB_INCLUDED_CSV = 1; * * @param rs the recordset * - * @return the CSV formated data + * @return the CSV formatted data */ function _rs2serialize(&$rs,$conn=false,$sql='') { @@ -85,7 +85,7 @@ $ADODB_INCLUDED_CSV = 1; * @param err returns the error message * @param timeout dispose if recordset has been alive for $timeout secs * -* @return recordset, or false if error occured. If no +* @return recordset, or false if error occurred. If no * error occurred in sql INSERT/UPDATE/DELETE, * empty recordset is returned */ diff --git a/adodb-datadict.inc.php b/adodb-datadict.inc.php index 4711f9be..5adb361e 100644 --- a/adodb-datadict.inc.php +++ b/adodb-datadict.inc.php @@ -22,11 +22,11 @@ // security - hide paths if (!defined('ADODB_DIR')) die(); -function Lens_ParseTest() +function lens_ParseTest() { $str = "`zcol ACOL` NUMBER(32,2) DEFAULT 'The \"cow\" (and Jim''s dog) jumps over the moon' PRIMARY, INTI INT AUTO DEFAULT 0, zcol2\"afs ds"; print "<p>$str</p>"; -$a= Lens_ParseArgs($str); +$a= lens_ParseArgs($str); print "<pre>"; print_r($a); print "</pre>"; @@ -53,7 +53,7 @@ if (!function_exists('ctype_alnum')) { @param tokenchars Include the following characters in tokens apart from A-Z and 0-9 @returns 2 dimensional array containing parsed tokens. */ -function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-') +function lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-') { $pos = 0; $intoken = false; @@ -179,45 +179,58 @@ class ADODB_DataDict { var $serverInfo = array(); var $autoIncrement = false; var $dataProvider; - var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changetablesql + var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changeTableSQL var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob - /// in other words, we use a text area for editting. + /// in other words, we use a text area for editing. - function GetCommentSQL($table,$col) + /* + * Indicates whether a BLOB/CLOB field will allow a NOT NULL setting + * The type is whatever is matched to an X or X2 or B type. We must + * explicitly set the value in the driver to switch the behaviour on + */ + public $blobAllowsNotNull; + /* + * Indicates whether a BLOB/CLOB field will allow a DEFAULT set + * The type is whatever is matched to an X or X2 or B type. We must + * explicitly set the value in the driver to switch the behaviour on + */ + public $blobAllowsDefaultValue; + + function getCommentSQL($table,$col) { return false; } - function SetCommentSQL($table,$col,$cmt) + function setCommentSQL($table,$col,$cmt) { return false; } - function MetaTables() + function metaTables() { - if (!$this->connection->IsConnected()) return array(); - return $this->connection->MetaTables(); + if (!$this->connection->isConnected()) return array(); + return $this->connection->metaTables(); } - function MetaColumns($tab, $upper=true, $schema=false) + function metaColumns($tab, $upper=true, $schema=false) { - if (!$this->connection->IsConnected()) return array(); - return $this->connection->MetaColumns($this->TableName($tab), $upper, $schema); + if (!$this->connection->isConnected()) return array(); + return $this->connection->metaColumns($this->tableName($tab), $upper, $schema); } - function MetaPrimaryKeys($tab,$owner=false,$intkey=false) + function metaPrimaryKeys($tab,$owner=false,$intkey=false) { - if (!$this->connection->IsConnected()) return array(); - return $this->connection->MetaPrimaryKeys($this->TableName($tab), $owner, $intkey); + if (!$this->connection->isConnected()) return array(); + return $this->connection->metaPrimaryKeys($this->tableName($tab), $owner, $intkey); } - function MetaIndexes($table, $primary = false, $owner = false) + function metaIndexes($table, $primary = false, $owner = false) { - if (!$this->connection->IsConnected()) return array(); - return $this->connection->MetaIndexes($this->TableName($table), $primary, $owner); + if (!$this->connection->isConnected()) return array(); + return $this->connection->metaIndexes($this->tableName($table), $primary, $owner); } - function MetaType($t,$len=-1,$fieldobj=false) + function metaType($t,$len=-1,$fieldobj=false) { static $typeMap = array( 'VARCHAR' => 'C', @@ -323,15 +336,15 @@ class ADODB_DataDict { "SQLBOOL" => 'L' ); - if (!$this->connection->IsConnected()) { + if (!$this->connection->isConnected()) { $t = strtoupper($t); if (isset($typeMap[$t])) return $typeMap[$t]; - return 'N'; + return ADODB_DEFAULT_METATYPE; } - return $this->connection->MetaType($t,$len,$fieldobj); + return $this->connection->metaType($t,$len,$fieldobj); } - function NameQuote($name = NULL,$allowBrackets=false) + function nameQuote($name = NULL,$allowBrackets=false) { if (!is_string($name)) { return FALSE; @@ -360,16 +373,16 @@ class ADODB_DataDict { return $name; } - function TableName($name) + function tableName($name) { if ( $this->schema ) { - return $this->NameQuote($this->schema) .'.'. $this->NameQuote($name); + return $this->nameQuote($this->schema) .'.'. $this->nameQuote($name); } - return $this->NameQuote($name); + return $this->nameQuote($name); } - // Executes the sql array returned by GetTableSQL and GetIndexSQL - function ExecuteSQLArray($sql, $continueOnError = true) + // Executes the sql array returned by getTableSQL and getIndexSQL + function executeSQLArray($sql, $continueOnError = true) { $rez = 2; $conn = $this->connection; @@ -377,10 +390,10 @@ class ADODB_DataDict { foreach($sql as $line) { if ($this->debug) $conn->debug = true; - $ok = $conn->Execute($line); + $ok = $conn->execute($line); $conn->debug = $saved; if (!$ok) { - if ($this->debug) ADOConnection::outp($conn->ErrorMsg()); + if ($this->debug) ADOConnection::outp($conn->errorMsg()); if (!$continueOnError) return 0; $rez = 1; } @@ -406,17 +419,17 @@ class ADODB_DataDict { N: Numeric or decimal number */ - function ActualType($meta) + function actualType($meta) { return $meta; } - function CreateDatabase($dbname,$options=false) + function createDatabase($dbname,$options=false) { - $options = $this->_Options($options); + $options = $this->_options($options); $sql = array(); - $s = 'CREATE DATABASE ' . $this->NameQuote($dbname); + $s = 'CREATE DATABASE ' . $this->nameQuote($dbname); if (isset($options[$this->upperName])) $s .= ' '.$options[$this->upperName]; @@ -427,7 +440,7 @@ class ADODB_DataDict { /* Generates the SQL to create index. Returns an array of sql strings. */ - function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false) + function createIndexSQL($idxname, $tabname, $flds, $idxoptions = false) { if (!is_array($flds)) { $flds = explode(',',$flds); @@ -435,27 +448,27 @@ class ADODB_DataDict { foreach($flds as $key => $fld) { # some indexes can use partial fields, eg. index first 32 chars of "name" with NAME(32) - $flds[$key] = $this->NameQuote($fld,$allowBrackets=true); + $flds[$key] = $this->nameQuote($fld,$allowBrackets=true); } - return $this->_IndexSQL($this->NameQuote($idxname), $this->TableName($tabname), $flds, $this->_Options($idxoptions)); + return $this->_indexSQL($this->nameQuote($idxname), $this->tableName($tabname), $flds, $this->_options($idxoptions)); } - function DropIndexSQL ($idxname, $tabname = NULL) + function dropIndexSQL ($idxname, $tabname = NULL) { - return array(sprintf($this->dropIndex, $this->NameQuote($idxname), $this->TableName($tabname))); + return array(sprintf($this->dropIndex, $this->nameQuote($idxname), $this->tableName($tabname))); } - function SetSchema($schema) + function setSchema($schema) { $this->schema = $schema; } - function AddColumnSQL($tabname, $flds) + function addColumnSQL($tabname, $flds) { - $tabname = $this->TableName ($tabname); + $tabname = $this->tableName($tabname); $sql = array(); - list($lines,$pkey,$idxs) = $this->_GenFields($flds); + list($lines,$pkey,$idxs) = $this->_genFields($flds); // genfields can return FALSE at times if ($lines == null) $lines = array(); $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' '; @@ -464,7 +477,7 @@ class ADODB_DataDict { } if (is_array($idxs)) { foreach($idxs as $idx => $idxdef) { - $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); + $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); $sql = array_merge($sql, $sql_idxs); } } @@ -474,19 +487,19 @@ class ADODB_DataDict { /** * Change the definition of one column * - * As some DBM's can't do that on there own, you need to supply the complete defintion of the new table, - * to allow, recreating the table and copying the content over to the new table + * As some DBMs can't do that on their own, you need to supply the complete definition of the new table, + * to allow recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column - * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default '' - * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default '' + * @param string $tableflds='' complete definition of the new table, eg. for postgres, default '' + * @param array/string $tableoptions='' options for the new table see createTableSQL, default '' * @return array with SQL strings */ - function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + function alterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { - $tabname = $this->TableName ($tabname); + $tabname = $this->tableName($tabname); $sql = array(); - list($lines,$pkey,$idxs) = $this->_GenFields($flds); + list($lines,$pkey,$idxs) = $this->_genFields($flds); // genfields can return FALSE at times if ($lines == null) $lines = array(); $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' '; @@ -495,7 +508,7 @@ class ADODB_DataDict { } if (is_array($idxs)) { foreach($idxs as $idx => $idxdef) { - $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); + $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); $sql = array_merge($sql, $sql_idxs); } @@ -506,71 +519,71 @@ class ADODB_DataDict { /** * Rename one column * - * Some DBM's can only do this together with changeing the type of the column (even if that stays the same, eg. mysql) + * Some DBMs can only do this together with changeing the type of the column (even if that stays the same, eg. mysql) * @param string $tabname table-name * @param string $oldcolumn column-name to be renamed * @param string $newcolumn new column-name - * @param string $flds='' complete column-defintion-string like for AddColumnSQL, only used by mysql atm., default='' + * @param string $flds='' complete column-definition-string like for addColumnSQL, only used by mysql atm., default='' * @return array with SQL strings */ - function RenameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='') + function renameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='') { - $tabname = $this->TableName ($tabname); + $tabname = $this->tableName($tabname); if ($flds) { - list($lines,$pkey,$idxs) = $this->_GenFields($flds); + list($lines,$pkey,$idxs) = $this->_genFields($flds); // genfields can return FALSE at times if ($lines == null) $lines = array(); $first = current($lines); list(,$column_def) = preg_split("/[\t ]+/",$first,2); } - return array(sprintf($this->renameColumn,$tabname,$this->NameQuote($oldcolumn),$this->NameQuote($newcolumn),$column_def)); + return array(sprintf($this->renameColumn,$tabname,$this->nameQuote($oldcolumn),$this->nameQuote($newcolumn),$column_def)); } /** * Drop one column * - * Some DBM's can't do that on there own, you need to supply the complete defintion of the new table, + * Some DBM's can't do that on their own, you need to supply the complete definition of the new table, * to allow, recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column - * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default '' - * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default '' + * @param string $tableflds='' complete definition of the new table, eg. for postgres, default '' + * @param array/string $tableoptions='' options for the new table see createTableSQL, default '' * @return array with SQL strings */ - function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + function dropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { - $tabname = $this->TableName ($tabname); + $tabname = $this->tableName($tabname); if (!is_array($flds)) $flds = explode(',',$flds); $sql = array(); $alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' '; foreach($flds as $v) { - $sql[] = $alter . $this->NameQuote($v); + $sql[] = $alter . $this->nameQuote($v); } return $sql; } - function DropTableSQL($tabname) + function dropTableSQL($tabname) { - return array (sprintf($this->dropTable, $this->TableName($tabname))); + return array (sprintf($this->dropTable, $this->tableName($tabname))); } - function RenameTableSQL($tabname,$newname) + function renameTableSQL($tabname,$newname) { - return array (sprintf($this->renameTable, $this->TableName($tabname),$this->TableName($newname))); + return array (sprintf($this->renameTable, $this->tableName($tabname),$this->tableName($newname))); } /** Generate the SQL to create table. Returns an array of sql strings. */ - function CreateTableSQL($tabname, $flds, $tableoptions=array()) + function createTableSQL($tabname, $flds, $tableoptions=array()) { - list($lines,$pkey,$idxs) = $this->_GenFields($flds, true); + list($lines,$pkey,$idxs) = $this->_genFields($flds, true); // genfields can return FALSE at times if ($lines == null) $lines = array(); - $taboptions = $this->_Options($tableoptions); - $tabname = $this->TableName ($tabname); - $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions); + $taboptions = $this->_options($tableoptions); + $tabname = $this->tableName($tabname); + $sql = $this->_tableSQL($tabname,$lines,$pkey,$taboptions); // ggiunta - 2006/10/12 - KLUDGE: // if we are on autoincrement, and table options includes REPLACE, the @@ -579,12 +592,12 @@ class ADODB_DataDict { // creating sql that double-drops the sequence if ($this->autoIncrement && isset($taboptions['REPLACE'])) unset($taboptions['REPLACE']); - $tsql = $this->_Triggers($tabname,$taboptions); + $tsql = $this->_triggers($tabname,$taboptions); foreach($tsql as $s) $sql[] = $s; if (is_array($idxs)) { foreach($idxs as $idx => $idxdef) { - $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); + $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); $sql = array_merge($sql, $sql_idxs); } } @@ -594,13 +607,13 @@ class ADODB_DataDict { - function _GenFields($flds,$widespacing=false) + function _genFields($flds,$widespacing=false) { if (is_string($flds)) { $padding = ' '; $txt = $flds.$padding; $flds = array(); - $flds0 = Lens_ParseArgs($txt,','); + $flds0 = lens_ParseArgs($txt,','); $hasparam = false; foreach($flds0 as $f0) { $f1 = array(); @@ -643,8 +656,8 @@ class ADODB_DataDict { $pkey = array(); $idxs = array(); foreach($flds as $fld) { - $fld = _array_change_key_case($fld); - + if (is_array($fld)) + $fld = array_change_key_case($fld,CASE_UPPER); $fname = false; $fdefault = false; $fautoinc = false; @@ -660,18 +673,23 @@ class ADODB_DataDict { $funsigned = false; $findex = ''; $funiqueindex = false; + $fOptions = array(); //----------------- // Parse attributes foreach($fld as $attr => $v) { - if ($attr == 2 && is_numeric($v)) $attr = 'SIZE'; - else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v); + if ($attr == 2 && is_numeric($v)) + $attr = 'SIZE'; + elseif ($attr == 2 && strtoupper($ftype) == 'ENUM') + $attr = 'ENUM'; + else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) + $attr = strtoupper($v); switch($attr) { case '0': case 'NAME': $fname = $v; break; case '1': - case 'TYPE': $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break; + case 'TYPE': $ty = $v; $ftype = $this->actualType(strtoupper($v)); break; case 'SIZE': $dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,','); @@ -697,6 +715,8 @@ class ADODB_DataDict { // let INDEX keyword create a 'very standard' index on column case 'INDEX': $findex = $v; break; case 'UNIQUE': $funiqueindex = true; break; + case 'ENUM': + $fOptions['ENUM'] = $v; break; } //switch } // foreach $fld @@ -708,7 +728,7 @@ class ADODB_DataDict { } $fid = strtoupper(preg_replace('/^`(.+)`$/', '$1', $fname)); - $fname = $this->NameQuote($fname); + $fname = $this->nameQuote($fname); if (!strlen($ftype)) { if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'"); @@ -717,14 +737,24 @@ class ADODB_DataDict { $ftype = strtoupper($ftype); } - $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec); + $ftype = $this->_getSize($ftype, $ty, $fsize, $fprec, $fOptions); - if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; // some blob types do not accept nulls + if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsNotNull) + /* + * some blob types do not accept nulls, so we override the + * previously defined value + */ + $fnotnull = false; - if ($fprimary) $pkey[] = $fname; + if ($fprimary) + $pkey[] = $fname; - // some databases do not allow blobs to have defaults - if ($ty == 'X') $fdefault = false; + if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsDefaultValue) + /* + * some databases do not allow blobs to have defaults, so we + * override the previously defined value + */ + $fdefault = false; // build list of indexes if ($findex != '') { @@ -769,11 +799,11 @@ class ADODB_DataDict { // convert default date into database-aware code if ($ty == 'T') { - $fdefault = $this->connection->DBTimeStamp($fdefault); + $fdefault = $this->connection->dbTimeStamp($fdefault); } else { - $fdefault = $this->connection->DBDate($fdefault); + $fdefault = $this->connection->dbDate($fdefault); } } else @@ -783,7 +813,7 @@ class ADODB_DataDict { $fdefault = $this->connection->qstr($fdefault); } } - $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned); + $suffix = $this->_createSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned); // add index creation if ($widespacing) $fname = str_pad($fname,24); @@ -806,19 +836,38 @@ class ADODB_DataDict { $ftype is the actual type $ty is the type defined originally in the DDL */ - function _GetSize($ftype, $ty, $fsize, $fprec) + function _getSize($ftype, $ty, $fsize, $fprec, $options=false) { if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) { $ftype .= "(".$fsize; if (strlen($fprec)) $ftype .= ",".$fprec; $ftype .= ')'; } + + /* + * Handle additional options + */ + if (is_array($options)) + { + foreach($options as $type=>$value) + { + switch ($type) + { + case 'ENUM': + $ftype .= '(' . $value . ')'; + break; + + default: + } + } + } + return $ftype; } // return string must begin with space - function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) + function _createSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) { $suffix = ''; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; @@ -827,7 +876,7 @@ class ADODB_DataDict { return $suffix; } - function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + function _indexSQL($idxname, $tabname, $flds, $idxoptions) { $sql = array(); @@ -856,25 +905,26 @@ class ADODB_DataDict { return $sql; } - function _DropAutoIncrement($tabname) + function _dropAutoIncrement($tabname) { return false; } - function _TableSQL($tabname,$lines,$pkey,$tableoptions) + function _tableSQL($tabname,$lines,$pkey,$tableoptions) { $sql = array(); if (isset($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) { $sql[] = sprintf($this->dropTable,$tabname); if ($this->autoIncrement) { - $sInc = $this->_DropAutoIncrement($tabname); + $sInc = $this->_dropAutoIncrement($tabname); if ($sInc) $sql[] = $sInc; } if ( isset ($tableoptions['DROP']) ) { return $sql; } } + $s = "CREATE TABLE $tabname (\n"; $s .= implode(",\n", $lines); if (sizeof($pkey)>0) { @@ -898,7 +948,7 @@ class ADODB_DataDict { GENERATE TRIGGERS IF NEEDED used when table has auto-incrementing field that is emulated using triggers */ - function _Triggers($tabname,$taboptions) + function _triggers($tabname,$taboptions) { return array(); } @@ -906,7 +956,7 @@ class ADODB_DataDict { /** Sanitize options, so that array elements with no keys are promoted to keys */ - function _Options($opts) + function _options($opts) { if (!is_array($opts)) return array(); $newopts = array(); @@ -938,25 +988,25 @@ class ADODB_DataDict { This function changes/adds new fields to your table. You don't have to know if the col is new or not. It will check on its own. */ - function ChangeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false) + function changeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; - if ($this->connection->fetchMode !== false) $savem = $this->connection->SetFetchMode(false); + if ($this->connection->fetchMode !== false) $savem = $this->connection->setFetchMode(false); // check table exists $save_handler = $this->connection->raiseErrorFn; $this->connection->raiseErrorFn = ''; - $cols = $this->MetaColumns($tablename); + $cols = $this->metaColumns($tablename); $this->connection->raiseErrorFn = $save_handler; - if (isset($savem)) $this->connection->SetFetchMode($savem); + if (isset($savem)) $this->connection->setFetchMode($savem); $ADODB_FETCH_MODE = $save; if ( empty($cols)) { - return $this->CreateTableSQL($tablename, $flds, $tableoptions); + return $this->createTableSQL($tablename, $flds, $tableoptions); } if (is_array($flds)) { @@ -976,7 +1026,7 @@ class ADODB_DataDict { $c = $cols[$k]; $ml = $c->max_length; - $mt = $this->MetaType($c->type,$ml); + $mt = $this->metaType($c->type,$ml); if (isset($c->scale)) $sc = $c->scale; else $sc = 99; // always force change if scale not known. @@ -998,16 +1048,16 @@ class ADODB_DataDict { // already exists, alter table instead - list($lines,$pkey,$idxs) = $this->_GenFields($flds); + list($lines,$pkey,$idxs) = $this->_genFields($flds); // genfields can return FALSE at times if ($lines == null) $lines = array(); - $alter = 'ALTER TABLE ' . $this->TableName($tablename); + $alter = 'ALTER TABLE ' . $this->tableName($tablename); $sql = array(); foreach ( $lines as $id => $v ) { if ( isset($cols[$id]) && is_object($cols[$id]) ) { - $flds = Lens_ParseArgs($v,','); + $flds = lens_ParseArgs($v,','); // We are trying to change the size of the field, if not allowed, simply ignore the request. // $flds[1] holds the type, $flds[2] holds the size -postnuke addition diff --git a/adodb-error.inc.php b/adodb-error.inc.php index 006829de..70529cc7 100644 --- a/adodb-error.inc.php +++ b/adodb-error.inc.php @@ -70,6 +70,10 @@ function adodb_error($provider,$dbType,$errno) case 'oracle': case 'oci8': $map = adodb_error_oci8(); break; + // As discussed in https://github.com/ADOdb/ADOdb/issues/201#issuecomment-188154980 + // firebird uses the ibase error handler for now. This may change if and + // when the PHP driver is updated to use the new SQLSTATE error codes + case 'firebird': case 'ibase': $map = adodb_error_ibase(); break; case 'odbc': $map = adodb_error_odbc(); break; @@ -245,9 +249,9 @@ static $MAP = array( 1006 => DB_ERROR_CANNOT_CREATE, 1007 => DB_ERROR_ALREADY_EXISTS, 1008 => DB_ERROR_CANNOT_DROP, - 1045 => DB_ERROR_ACCESS_VIOLATION, + 1045 => DB_ERROR_ACCESS_VIOLATION, 1046 => DB_ERROR_NODBSELECTED, - 1049 => DB_ERROR_NOSUCHDB, + 1049 => DB_ERROR_NOSUCHDB, 1050 => DB_ERROR_ALREADY_EXISTS, 1051 => DB_ERROR_NOSUCHTABLE, 1054 => DB_ERROR_NOSUCHFIELD, @@ -257,8 +261,8 @@ static $MAP = array( 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, 1146 => DB_ERROR_NOSUCHTABLE, 1048 => DB_ERROR_CONSTRAINT, - 2002 => DB_ERROR_CONNECT_FAILED, - 2005 => DB_ERROR_CONNECT_FAILED + 2002 => DB_ERROR_CONNECT_FAILED, + 2005 => DB_ERROR_CONNECT_FAILED ); return $MAP; diff --git a/adodb-errorpear.inc.php b/adodb-errorpear.inc.php index 4f1c98a7..404c6a81 100644 --- a/adodb-errorpear.inc.php +++ b/adodb-errorpear.inc.php @@ -78,7 +78,7 @@ global $ADODB_Last_PEAR_Error; /** * Returns last PEAR_Error object. This error might be for an error that -* occured several sql statements ago. +* occurred several sql statements ago. */ function ADODB_PEAR_Error() { diff --git a/adodb-exceptions.inc.php b/adodb-exceptions.inc.php index 63709d0f..b394c138 100644 --- a/adodb-exceptions.inc.php +++ b/adodb-exceptions.inc.php @@ -60,14 +60,14 @@ var $database = ''; } /** -* Default Error Handler. This will be called with the following params +* Default Error Handler. * -* @param $dbms the RDBMS you are connecting to -* @param $fn the name of the calling function (in uppercase) -* @param $errno the native error number from the database -* @param $errmsg the native error msg from the database -* @param $p1 $fn specific parameter - see below -* @param $P2 $fn specific parameter - see below +* @param string $dbms the RDBMS you are connecting to +* @param string $fn the name of the calling function (in uppercase) +* @param int $errno the native error number from the database +* @param string $errmsg the native error msg from the database +* @param mixed $p1 $fn specific parameter - see below +* @param mixed $p2 $fn specific parameter - see below */ function adodb_throw($dbms, $fn, $errno, $errmsg, $p1, $p2, $thisConnection) diff --git a/adodb-lib.inc.php b/adodb-lib.inc.php index 98b7d798..ecdf9c0d 100644 --- a/adodb-lib.inc.php +++ b/adodb-lib.inc.php @@ -19,7 +19,12 @@ $ADODB_INCLUDED_LIB = 1; function adodb_strip_order_by($sql) { - $rez = preg_match('/(\sORDER\s+BY\s(?:[^)](?!LIMIT))*)/is', $sql, $arr); + $rez = preg_match_all('/(\sORDER\s+BY\s(?:[^)](?!LIMIT))*)/is', $sql, $arr); + if ($arr) + { + $tmp = array_pop($arr); + $arr = [1=>array_pop($tmp)]; + } if ($arr) if (strpos($arr[1], '(') !== false) { $at = strpos($sql, $arr[1]); @@ -39,6 +44,7 @@ function adodb_strip_order_by($sql) } else { $sql = str_replace($arr[1], '', $sql); } + return $sql; } @@ -118,98 +124,79 @@ function adodb_transpose(&$arr, &$newarr, &$hdr, &$fobjs) } } -// Force key to upper. -// See also http://www.php.net/manual/en/function.array-change-key-case.php -function _array_change_key_case($an_array) -{ - if (is_array($an_array)) { - $new_array = array(); - foreach($an_array as $key=>$value) - $new_array[strtoupper($key)] = $value; - - return $new_array; - } - - return $an_array; -} function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc) { - if (count($fieldArray) == 0) return 0; - $first = true; - $uSet = ''; + // Add Quote around table name to support use of spaces / reserved keywords + $table=sprintf('%s%s%s', $zthis->nameQuote,$table,$zthis->nameQuote); - if (!is_array($keyCol)) { - $keyCol = array($keyCol); - } - foreach($fieldArray as $k => $v) { - if ($v === null) { - $v = 'NULL'; - $fieldArray[$k] = $v; - } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) { - $v = $zthis->qstr($v); - $fieldArray[$k] = $v; - } - if (in_array($k,$keyCol)) continue; // skip UPDATE if is key + if (count($fieldArray) == 0) return 0; - if ($first) { - $first = false; - $uSet = "$k=$v"; - } else - $uSet .= ",$k=$v"; + if (!is_array($keyCol)) { + $keyCol = array($keyCol); + } + $uSet = ''; + foreach($fieldArray as $k => $v) { + if ($v === null) { + $v = 'NULL'; + $fieldArray[$k] = $v; + } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) { + $v = $zthis->qstr($v); + $fieldArray[$k] = $v; } + if (in_array($k,$keyCol)) continue; // skip UPDATE if is key - $where = false; - foreach ($keyCol as $v) { - if (isset($fieldArray[$v])) { - if ($where) $where .= ' and '.$v.'='.$fieldArray[$v]; - else $where = $v.'='.$fieldArray[$v]; - } - } + // Add Quote around column name to support use of spaces / reserved keywords + $uSet .= sprintf(',%s%s%s=%s',$zthis->nameQuote,$k,$zthis->nameQuote,$v); + } + $uSet = ltrim($uSet, ','); - if ($uSet && $where) { - $update = "UPDATE $table SET $uSet WHERE $where"; + // Add Quote around column name in where clause + $where = ''; + foreach ($keyCol as $v) { + if (isset($fieldArray[$v])) { + $where .= sprintf(' and %s%s%s=%s ', $zthis->nameQuote,$v,$zthis->nameQuote,$fieldArray[$v]); + } + } + if ($where) { + $where = substr($where, 5); + } - $rs = $zthis->Execute($update); + if ($uSet && $where) { + $update = "UPDATE $table SET $uSet WHERE $where"; + $rs = $zthis->Execute($update); + if ($rs) { + if ($zthis->poorAffectedRows) { + // The Select count(*) wipes out any errors that the update would have returned. + // http://phplens.com/lens/lensforum/msgs.php?id=5696 + if ($zthis->ErrorNo()<>0) return 0; - if ($rs) { - if ($zthis->poorAffectedRows) { - /* - The Select count(*) wipes out any errors that the update would have returned. - http://phplens.com/lens/lensforum/msgs.php?id=5696 - */ - if ($zthis->ErrorNo()<>0) return 0; + // affected_rows == 0 if update field values identical to old values + // for mysql - which is silly. + $cnt = $zthis->GetOne("select count(*) from $table where $where"); + if ($cnt > 0) return 1; // record already exists + } else { + if (($zthis->Affected_Rows()>0)) return 1; + } + } else + return 0; + } - # affected_rows == 0 if update field values identical to old values - # for mysql - which is silly. + $iCols = $iVals = ''; + foreach($fieldArray as $k => $v) { + if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col - $cnt = $zthis->GetOne("select count(*) from $table where $where"); - if ($cnt > 0) return 1; // record already exists - } else { - if (($zthis->Affected_Rows()>0)) return 1; - } - } else - return 0; - } - - // print "<p>Error=".$this->ErrorNo().'<p>'; - $first = true; - foreach($fieldArray as $k => $v) { - if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col + // Add Quote around Column Name + $iCols .= sprintf(',%s%s%s',$zthis->nameQuote,$k,$zthis->nameQuote); + $iVals .= ",$v"; + } + $iCols = ltrim($iCols, ','); + $iVals = ltrim($iVals, ','); - if ($first) { - $first = false; - $iCols = "$k"; - $iVals = "$v"; - } else { - $iCols .= ",$k"; - $iVals .= ",$v"; - } - } - $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)"; - $rs = $zthis->Execute($insert); - return ($rs) ? 2 : 0; + $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)"; + $rs = $zthis->Execute($insert); + return ($rs) ? 2 : 0; } function _adodb_getmenu(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false, @@ -476,18 +463,11 @@ function _adodb_getcount(&$zthis, $sql,$inputarr=false,$secs2cache=0) if (!$rstest) $rstest = $zthis->Execute($sql,$inputarr); } if ($rstest) { - $qryRecs = $rstest->RecordCount(); + $qryRecs = $rstest->RecordCount(); if ($qryRecs == -1) { - global $ADODB_EXTENSION; - // some databases will return -1 on MoveLast() - change to MoveNext() - if ($ADODB_EXTENSION) { - while(!$rstest->EOF) { - adodb_movenext($rstest); - } - } else { - while(!$rstest->EOF) { - $rstest->MoveNext(); - } + // some databases will return -1 on MoveLast() - change to MoveNext() + while(!$rstest->EOF) { + $rstest->MoveNext(); } $qryRecs = $rstest->_currentRow; } @@ -650,58 +630,59 @@ function _adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr return $rsreturn; } -function _adodb_getupdatesql(&$zthis,&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=2) +function _adodb_getupdatesql(&$zthis, &$rs, $arrFields, $forceUpdate=false, $magicq=false, $force=2) { global $ADODB_QUOTE_FIELDNAMES; - if (!$rs) { - printf(ADODB_BAD_RS,'GetUpdateSQL'); - return false; - } - - $fieldUpdatedCount = 0; - $arrFields = _array_change_key_case($arrFields); - - $hasnumeric = isset($rs->fields[0]); - $setFields = ''; + if (!$rs) { + printf(ADODB_BAD_RS,'GetUpdateSQL'); + return false; + } - // Loop through all of the fields in the recordset - for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { - // Get the field from the recordset - $field = $rs->FetchField($i); + $fieldUpdatedCount = 0; + if (is_array($arrFields)) + $arrFields = array_change_key_case($arrFields,CASE_UPPER); - // If the recordset field is one - // of the fields passed in then process. - $upperfname = strtoupper($field->name); - if (adodb_key_exists($upperfname,$arrFields,$force)) { + $hasnumeric = isset($rs->fields[0]); + $setFields = ''; - // If the existing field value in the recordset - // is different from the value passed in then - // go ahead and append the field name and new value to - // the update query. + // Loop through all of the fields in the recordset + for ($i=0, $max=$rs->fieldCount(); $i < $max; $i++) { + // Get the field from the recordset + $field = $rs->fetchField($i); - if ($hasnumeric) $val = $rs->fields[$i]; - else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname]; - else if (isset($rs->fields[$field->name])) $val = $rs->fields[$field->name]; - else if (isset($rs->fields[strtolower($upperfname)])) $val = $rs->fields[strtolower($upperfname)]; - else $val = ''; + // If the recordset field is one + // of the fields passed in then process. + $upperfname = strtoupper($field->name); + if (adodb_key_exists($upperfname, $arrFields, $force)) { + // If the existing field value in the recordset + // is different from the value passed in then + // go ahead and append the field name and new value to + // the update query. - if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) { - // Set the counter for the number of fields that will be updated. - $fieldUpdatedCount++; + if ($hasnumeric) $val = $rs->fields[$i]; + else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname]; + else if (isset($rs->fields[$field->name])) $val = $rs->fields[$field->name]; + else if (isset($rs->fields[strtolower($upperfname)])) $val = $rs->fields[strtolower($upperfname)]; + else $val = ''; - // Based on the datatype of the field - // Format the value properly for the database - $type = $rs->MetaType($field->type); + if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) { + // Set the counter for the number of fields that will be updated. + $fieldUpdatedCount++; + // Based on the datatype of the field + // Format the value properly for the database + $type = $rs->metaType($field->type); - if ($type == 'null') { - $type = 'C'; - } + if ($type == 'null') { + $type = 'C'; + } - if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) { - switch ($ADODB_QUOTE_FIELDNAMES) { + if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) { + switch ($ADODB_QUOTE_FIELDNAMES) { + case 'BRACKETS': + $fnameq = $zthis->leftBracket.$upperfname.$zthis->rightBracket;break; case 'LOWER': $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break; case 'NATIVE': @@ -709,89 +690,107 @@ function _adodb_getupdatesql(&$zthis,&$rs, $arrFields,$forceUpdate=false,$magicq case 'UPPER': default: $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;break; - } - } else - $fnameq = $upperfname; + } + } else { + $fnameq = $upperfname; + } - //********************************************************// - if (is_null($arrFields[$upperfname]) + //********************************************************// + if (is_null($arrFields[$upperfname]) || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0) - || $arrFields[$upperfname] === $zthis->null2null - ) - { - switch ($force) { + || $arrFields[$upperfname] === $zthis->null2null + ) { - //case 0: - // //Ignore empty values. This is allready handled in "adodb_key_exists" function. - //break; + switch ($force) { - case 1: - //Set null - $setFields .= $field->name . " = null, "; - break; + //case 0: + // // Ignore empty values. This is already handled in "adodb_key_exists" function. + // break; + + case 1: + // set null + $setFields .= $fnameq . " = null, "; + break; + + case 2: + // set empty + $arrFields[$upperfname] = ""; + $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields, $magicq); + break; - case 2: - //Set empty - $arrFields[$upperfname] = ""; - $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq); - break; default: - case 3: - //Set the value that was given in array, so you can give both null and empty values - if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) { - $setFields .= $field->name . " = null, "; - } else { - $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq); - } - break; - } - //********************************************************// - } else { - //we do this so each driver can customize the sql for - //DB specific column types. - //Oracle needs BLOB types to be handled with a returning clause - //postgres has special needs as well - $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, - $arrFields, $magicq); + case 3: + // set the value that was given in array, so you can give both null and empty values + if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) { + $setFields .= $fnameq . " = null, "; + } else { + $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields, $magicq); + } + break; + + case ADODB_FORCE_NULL_AND_ZERO: + + switch ($type) { + case 'N': + case 'I': + case 'L': + $setFields .= $fnameq . ' = 0, '; + break; + default: + $setFields .= $fnameq . ' = null, '; + break; + } + break; + } + //********************************************************// + } else { + // we do this so each driver can customize the sql for + // DB specific column types. + // Oracle needs BLOB types to be handled with a returning clause + // postgres has special needs as well + $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields, $magicq); } } } + } - // If there were any modified fields then build the rest of the update query. - if ($fieldUpdatedCount > 0 || $forceUpdate) { - // Get the table name from the existing query. - if (!empty($rs->tableName)) $tableName = $rs->tableName; - else { - preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName); - $tableName = $tableName[1]; - } - // Get the full where clause excluding the word "WHERE" from - // the existing query. - preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause); - - $discard = false; - // not a good hack, improvements? - if ($whereClause) { - #var_dump($whereClause); - if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard)); - else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard)); - else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard)); - else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see https://sourceforge.net/p/adodb/bugs/37/ - } else - $whereClause = array(false,false); + // If there were any modified fields then build the rest of the update query. + if ($fieldUpdatedCount > 0 || $forceUpdate) { + // Get the table name from the existing query. + if (!empty($rs->tableName)) { + $tableName = $rs->tableName; + } else { + preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName); + $tableName = $tableName[1]; + } - if ($discard) - $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1])); + // Get the full where clause excluding the word "WHERE" from the existing query. + preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause); - $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2); - if (strlen($whereClause[1]) > 0) - $sql .= ' WHERE '.$whereClause[1]; + $discard = false; + // not a good hack, improvements? + if ($whereClause) { + #var_dump($whereClause); + if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard)); + else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard)); + else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard)); + else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see https://sourceforge.net/p/adodb/bugs/37/ + } else { + $whereClause = array(false, false); + } - return $sql; + if ($discard) { + $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1])); + } - } else { - return false; + $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2); + if (strlen($whereClause[1]) > 0) { + $sql .= ' WHERE '.$whereClause[1]; + } + return $sql; + } else { + return false; } } @@ -802,10 +801,10 @@ function adodb_key_exists($key, &$arr,$force=2) return (!empty($arr[$key])) || (isset($arr[$key]) && strlen($arr[$key])>0); } - if (isset($arr[$key])) return true; + if (isset($arr[$key])) + return true; ## null check below - if (ADODB_PHPVER >= 0x4010) return array_key_exists($key,$arr); - return false; + return array_key_exists($key,$arr); } /** @@ -826,7 +825,8 @@ static $cacheCols; $values = ''; $fields = ''; $recordSet = null; - $arrFields = _array_change_key_case($arrFields); + if (is_array($arrFields)) + $arrFields = array_change_key_case($arrFields,CASE_UPPER); $fieldInsertedCount = 0; if (is_string($rs)) { @@ -872,6 +872,8 @@ static $cacheCols; $bad = false; if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) { switch ($ADODB_QUOTE_FIELDNAMES) { + case 'BRACKETS': + $fnameq = $zthis->leftBracket.$upperfname.$zthis->rightBracket;break; case 'LOWER': $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break; case 'NATIVE': @@ -893,22 +895,22 @@ static $cacheCols; { switch ($force) { - case 0: // we must always set null if missing + case ADODB_FORCE_IGNORE: // we must always set null if missing $bad = true; break; - case 1: + case ADODB_FORCE_NULL: $values .= "null, "; break; - case 2: + case ADODB_FORCE_EMPTY: //Set empty $arrFields[$upperfname] = ""; $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,$arrFields, $magicq); break; default: - case 3: + case ADODB_FORCE_VALUE: //Set the value that was given in array, so you can give both null and empty values if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) { $values .= "null, "; @@ -916,6 +918,21 @@ static $cacheCols; $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields, $magicq); } break; + + case ADODB_FORCE_NULL_AND_ZERO: + switch ($type) + { + case 'N': + case 'I': + case 'L': + $values .= '0, '; + break; + default: + $values .= "null, "; + break; + } + break; + } // switch /*********************************************************/ diff --git a/adodb-loadbalancer.inc.php b/adodb-loadbalancer.inc.php new file mode 100644 index 00000000..a4e59912 --- /dev/null +++ b/adodb-loadbalancer.inc.php @@ -0,0 +1,773 @@ +<?php +/** + * ADOdb Load Balancer + * + * ADOdbLoadBalancer is a class that allows the user to do read/write splitting + * and load balancing across multiple servers. It can handle and load balance + * any number of write capable (AKA: master) or readonly (AKA: slave) connections, + * including dealing with connection failures and retrying queries on a different + * connection instead. + * + * Released under both BSD license and Lesser GPL library license. + * Whenever there is any discrepancy between the two licenses, + * the BSD license will take precedence. See LICENSE.md. + * + * Latest version is available at https://adodb.org/ + * + * @package ADOdb + * @version v5.21.0-dev ??-???-2016 + * @author Mike Benoit + * @copyright (c) 2016 Mike Benoit and the ADOdb community + * @license BSD-3-Clause + * @license GNU Lesser General Public License (LGPL) v2.1 or later + * @link https://adodb.org/ + * @since v5.21.0 + */ + +/** + * Class ADOdbLoadBalancer + */ +class ADOdbLoadBalancer +{ + /** + * @var bool Once a write or readonly connection is made, stick to that connection for the entire request. + */ + public $enable_sticky_sessions = true; + + + /** + * @var bool|array All connections to each database. + */ + protected $connections = false; + + /** + * @var bool|array Just connections to the write capable database. + */ + protected $connections_write = false; + + /** + * @var bool|array Just connections to the readonly database. + */ + protected $connections_readonly = false; + + /** + * @var array Counts of all connections and their types. + */ + protected $total_connections = array('all' => 0, 'write' => 0, 'readonly' => 0); + + /** + * @var array Weights of all connections for each type. + */ + protected $total_connection_weights = array('all' => 0, 'write' => 0, 'readonly' => 0); + + /** + * @var bool When in transactions, always use this connection. + */ + protected $pinned_connection_id = false; + + /** + * @var array Last connection_id for each database type. + */ + protected $last_connection_id = array('write' => false, 'readonly' => false, 'all' => false); + + /** + * @var bool Session variables that must be maintained across all connections, ie: SET TIME ZONE. + */ + protected $session_variables = false; + + /** + * @var bool Called immediately after connecting to any DB. + */ + protected $user_defined_session_init_sql = false; + + + /** + * Defines SQL queries that are executed each time a new database connection is established. + * + * @param $sql + * @return bool + */ + public function setSessionInitSQL($sql) + { + $this->user_defined_session_init_sql[] = $sql; + + return true; + } + + /** + * Adds a new database connection to the pool, but no actual connection is made until its needed. + * + * @param $obj + * @return bool + * @throws Exception + */ + public function addConnection($obj) + { + if ($obj instanceof ADOdbLoadBalancerConnection) { + $this->connections[] = $obj; + end($this->connections); + $i = key($this->connections); + + $this->total_connections[$obj->type]++; + $this->total_connections['all']++; + + $this->total_connection_weights[$obj->type] += abs($obj->weight); + $this->total_connection_weights['all'] += abs($obj->weight); + + if ($obj->type == 'write') { + $this->connections_write[] = $i; + } else { + $this->connections_readonly[] = $i; + } + + return true; + } + + throw new Exception('Connection object is not an instance of ADOdbLoadBalancerConnection'); + } + + /** + * Removes a database connection from the pool. + * + * @param $i + * @return bool + */ + public function removeConnection($i) + { + if (isset($this->connections[$i])) { + $obj = $this->connections[ $i ]; + + $this->total_connections[ $obj->type ]--; + $this->total_connections['all']--; + + $this->total_connection_weights[ $obj->type ] -= abs($obj->weight); + $this->total_connection_weights['all'] -= abs($obj->weight); + + if ($obj->type == 'write') { + unset($this->connections_write[array_search($i, $this->connections_write)]); + // Reindex array. + $this->connections_write = array_values($this->connections_write); + } else { + unset($this->connections_readonly[array_search($i, $this->connections_readonly)]); + // Reindex array. + $this->connections_readonly = array_values($this->connections_readonly); + } + + // Remove any sticky connections as well. + if ($this->last_connection_id[$obj->type] == $i) { + $this->last_connection_id[$obj->type] = false; + } + + unset($this->connections[$i]); + + return true; + } + + return false; + } + + /** + * Returns a database connection of the specified type. + * + * Takes into account the connection weight for load balancing. + * + * @param string $type Type of database connection, either: 'write' capable or 'readonly' + * @return bool|int|string + */ + private function getConnectionByWeight($type) + { + if ($type == 'readonly') { + $total_weight = $this->total_connection_weights['all']; + } else { + $total_weight = $this->total_connection_weights['write']; + } + + $i = false; + if (is_array($this->connections)) { + $n = 0; + $num = mt_rand(0, $total_weight); + foreach ($this->connections as $i => $connection_obj) { + if ($connection_obj->weight > 0 && ($type == 'readonly' || $connection_obj->type == 'write')) { + $n += $connection_obj->weight; + if ($n >= $num) { + break; + } + } + } + } + + return $i; + } + + /** + * Returns the proper database connection when taking into account sticky sessions and load balancing. + * + * @param $type + * @return bool|int|mixed|string + */ + public function getLoadBalancedConnection($type) + { + if ($this->total_connections == 0) { + $connection_id = 0; + } else { + if ($this->enable_sticky_sessions == true && $this->last_connection_id[$type] !== false) { + $connection_id = $this->last_connection_id[$type]; + } else { + if ($type == 'write' && $this->total_connections['write'] == 1) { + $connection_id = $this->connections_write[0]; + } else { + $connection_id = $this->getConnectionByWeight($type); + } + } + } + + return $connection_id; + } + + /** + * Returns the ADODB connection object by connection_id. + * + * Ensures that it's connected and the session variables are executed. + * + * @param $connection_id + * @return bool|ADOConnection + * @throws Exception + */ + private function _getConnection($connection_id) + { + if (isset($this->connections[$connection_id])) { + $connection_obj = $this->connections[$connection_id]; + /** @var ADOConnection $adodb_obj */ + $adodb_obj = $connection_obj->getADOdbObject(); + if (is_object($adodb_obj) && $adodb_obj->_connectionID == false) { + try { + if ($connection_obj->persistent_connection == true) { + $adodb_obj->Pconnect( + $connection_obj->host, + $connection_obj->user, + $connection_obj->password, + $connection_obj->database + ); + } else { + $adodb_obj->Connect( + $connection_obj->host, + $connection_obj->user, + $connection_obj->password, + $connection_obj->database + ); + } + } catch (Exception $e) { + // Connection error, see if there are other connections to try still. + throw $e; // No connections left, reThrow exception so application can catch it. + } + + if (is_array($this->user_defined_session_init_sql)) { + foreach ($this->user_defined_session_init_sql as $session_init_sql) { + $adodb_obj->Execute($session_init_sql); + } + } + $this->executeSessionVariables($adodb_obj); + } + + return $adodb_obj; + } else { + throw new Exception('Unable to return Connection object...'); + } + } + + /** + * Returns the ADODB connection object by database type. + * + * Ensures that it's connected and the session variables are executed. + * + * @param string $type + * @param null $pin_connection + * @return ADOConnection|bool + * @throws Exception + */ + public function getConnection($type = 'write', $pin_connection = null) + { + while (($type == 'write' && $this->total_connections['write'] > 0) + || ($type == 'readonly' && $this->total_connections['all'] > 0) + ) { + if ($this->pinned_connection_id !== false) { + $connection_id = $this->pinned_connection_id; + } else { + $connection_id = $this->getLoadBalancedConnection($type); + } + + if ($connection_id !== false) { + try { + $adodb_obj = $this->_getConnection($connection_id); + // $connection_obj = $this->connections[$connection_id]; + break; + } catch (Exception $e) { + // Connection error, see if there are other connections to try still. + $this->removeConnection($connection_id); + if ( ($type == 'write' && $this->total_connections['write'] == 0) + || ($type == 'readonly' && $this->total_connections['all'] == 0) + ) { + throw $e; + } + } + } else { + throw new Exception('Connection ID is invalid!'); + } + } + + $this->last_connection_id[$type] = $connection_id; + + if ($pin_connection === true) { + $this->pinned_connection_id = $connection_id; + } elseif ($pin_connection === false && $adodb_obj->transOff <= 1) { + // UnPin connection only if we are 1 level deep in a transaction. + $this->pinned_connection_id = false; + + // When unpinning connection, reset last_connection_id so readonly + // queries don't get stuck on the write capable connection. + $this->last_connection_id['write'] = false; + $this->last_connection_id['readonly'] = false; + } + + return $adodb_obj; + } + + /** + * This is a hack to work around pass by reference error. + * + * Parameter 1 to ADOConnection::GetInsertSQL() expected to be a reference, + * value given in adodb-loadbalancer.inc.php on line 83 + * + * @param $arr + * @return array + */ + private function makeValuesReferenced($arr) + { + $refs = array(); + + foreach ($arr as $key => $value) { + $refs[$key] = &$arr[$key]; + } + + return $refs; + } + + /** + * Allow setting session variables that are maintained across connections. + * + * Its important that these are set using name/value, so it can determine + * if the same variable is set multiple times causing bloat/clutter when + * new connections are established. For example if the time_zone is set to + * many different ones through the course of a single connection, a new + * connection should only set it to the most recent value. + * + * @param $name + * @param $value + * @param bool $execute_immediately + * @return array|bool|mixed + * @throws Exception + */ + public function setSessionVariable($name, $value, $execute_immediately = true) + { + $this->session_variables[$name] = $value; + + if ($execute_immediately == true) { + return $this->executeSessionVariables(); + } else { + return true; + } + } + + /** + * Executes the session variables on a given ADODB object. + * + * @param ADOConnection|bool $adodb_obj + * @return array|bool|mixed + * @throws Exception + */ + private function executeSessionVariables($adodb_obj = false) + { + if (is_array($this->session_variables)) { + $sql = ''; + foreach ($this->session_variables as $name => $value) { + // $sql .= 'SET SESSION '. $name .' '. $value; + // MySQL uses: SET SESSION foo_bar='foo' + // PGSQL uses: SET SESSION foo_bar 'foo' + // So leave it up to the user to pass the proper value with '=' if needed. + // This may be a candidate to move into ADOdb proper. + $sql .= 'SET SESSION ' . $name . ' ' . $value; + } + + if ($adodb_obj !== false) { + return $adodb_obj->Execute($sql); + } else { + return $this->ClusterExecute($sql); + } + } + + return false; + } + + /** + * Executes the same SQL QUERY on the entire cluster of connections. + * Would be used for things like SET SESSION TIME ZONE calls and such. + * + * @param $sql + * @param bool $inputarr + * @param bool $return_all_results + * @param bool $existing_connections_only + * @return array|bool|mixed + * @throws Exception + */ + public function clusterExecute( + $sql, + $inputarr = false, + $return_all_results = false, + $existing_connections_only = true + ) { + if (is_array($this->connections) && count($this->connections) > 0) { + foreach ($this->connections as $key => $connection_obj) { + if ($existing_connections_only == false + || ($existing_connections_only == true + && $connection_obj->getADOdbObject()->_connectionID !== false + ) + ) { + $adodb_obj = $this->_getConnection($key); + if (is_object($adodb_obj)) { + $result_arr[] = $adodb_obj->Execute($sql, $inputarr); + } + } + } + + if (isset($result_arr) && $return_all_results == true) { + return $result_arr; + } else { + // Loop through all results checking to see if they match, if they do return the first one + // otherwise return an array of all results. + if (isset($result_arr)) { + foreach ($result_arr as $result) { + if ($result == false) { + return $result_arr; + } + } + + return $result_arr[0]; + } else { + // When using lazy connections, there are cases where + // setSessionVariable() is called early on, but there are + // no connections to execute the queries on yet. + // This captures that case and forces a RETURN TRUE to occur. + // As likely the queries will be executed as soon as a + // connection is established. + return true; + } + } + } + + return false; + } + + /** + * Determines if a SQL query is read-only or not. + * + * @param string $sql SQL Query to test. + * @return bool + */ + public function isReadOnlyQuery($sql) + { + if ( stripos($sql, 'SELECT') === 0 + && stripos($sql, 'FOR UPDATE') === false + && stripos($sql, ' INTO ') === false + && stripos($sql, 'LOCK IN') === false + ) { + return true; + } + + return false; + } + + /** + * Use this instead of __call() as it significantly reduces the overhead of call_user_func_array(). + * + * @param $sql + * @param bool $inputarr + * @return array|bool|mixed + * @throws Exception + */ + public function execute($sql, $inputarr = false) + { + $type = 'write'; + $pin_connection = null; + + // Prevent leading spaces from causing isReadOnlyQuery/stripos from failing. + $sql = trim($sql); + + // SELECT queries that can write and therefore must be run on a write capable connection. + // SELECT ... FOR UPDATE; + // SELECT ... INTO ... + // SELECT .. LOCK IN ... (MYSQL) + if ($this->isReadOnlyQuery($sql) == true) { + $type = 'readonly'; + } elseif (stripos($sql, 'SET') === 0) { + // SET SQL statements should likely use setSessionVariable() instead, + // so state is properly maintained across connections, especially when they are lazily created. + return $this->ClusterExecute($sql, $inputarr); + } + + $adodb_obj = $this->getConnection($type, $pin_connection); + if ($adodb_obj !== false) { + return $adodb_obj->Execute($sql, $inputarr); + } + + return false; + } + + /** + * Magic method to intercept method and callback to the proper ADODB object for write/readonly connections. + * + * @param string $method ADODB method to call. + * @param array $args Arguments to the ADODB method. + * @return bool|mixed + * @throws Exception + */ + public function __call($method, $args) + { + $type = 'write'; + $pin_connection = null; + + // Intercept specific methods to determine if they are read-only or not. + $method = strtolower($method); + switch ($method) { + // case 'execute': // This is the direct overloaded function above instead. + case 'getone': + case 'getrow': + case 'getall': + case 'getcol': + case 'getassoc': + case 'selectlimit': + if ($this->isReadOnlyQuery(trim($args[0])) == true) { + $type = 'readonly'; + } + break; + case 'cachegetone': + case 'cachegetrow': + case 'cachegetall': + case 'cachegetcol': + case 'cachegetassoc': + case 'cacheexecute': + case 'cacheselect': + case 'pageexecute': + case 'cachepageexecute': + $type = 'readonly'; + break; + // case 'ignoreerrors': + // // When ignoreerrors is called, PIN to the connection until its called again. + // if (!isset($args[0]) || (isset($args[0]) && $args[0] == FALSE)) { + // $pin_connection = TRUE; + // } else { + // $pin_connection = FALSE; + // } + // break; + + // Manual transactions + case 'begintrans': + case 'settransactionmode': + $pin_connection = true; + break; + case 'rollbacktrans': + case 'committrans': + $pin_connection = false; + break; + // Smart transactions + case 'starttrans': + $pin_connection = true; + break; + case 'completetrans': + case 'failtrans': + // getConnection() will only unpin the transaction if we're exiting the last nested transaction + $pin_connection = false; + break; + + // Functions that don't require any connection and therefore + // shouldn't force a connection be established before they run. + case 'qstr': + case 'escape': + case 'binddate': + case 'bindtimestamp': + case 'setfetchmode': + $type = false; // No connection necessary. + break; + + // Default to assuming write connection is required to be on the safe side. + default: + break; + } + + if ($type === false) { + if (is_array($this->connections) && count($this->connections) > 0) { + foreach ($this->connections as $key => $connection_obj) { + $adodb_obj = $connection_obj->getADOdbObject(); + return call_user_func_array(array($adodb_obj, $method), $this->makeValuesReferenced($args)); // Just makes the function call on the first object. + } + } + } else { + $adodb_obj = $this->getConnection($type, $pin_connection); + if (is_object($adodb_obj)) { + $result = call_user_func_array(array($adodb_obj, $method), $this->makeValuesReferenced($args)); + + return $result; + } + } + return false; + } + + /** + * Magic method to proxy property getter calls back to the proper ADODB object currently in use. + * + * @param $property + * @return mixed + * @throws Exception + */ + public function __get($property) + { + if (is_array($this->connections) && count($this->connections) > 0) { + foreach ($this->connections as $key => $connection_obj) { + // Just returns the property from the first object. + return $connection_obj->getADOdbObject()->$property; + } + } + + return false; + } + + /** + * Magic method to proxy property setter calls back to the proper ADODB object currently in use. + * + * @param $property + * @param $value + * @return mixed + * @throws Exception + */ + public function __set($property, $value) + { + // Special function to set object properties on all objects + // without initiating a connection to the database. + if (is_array($this->connections) && count($this->connections) > 0) { + foreach ($this->connections as $key => $connection_obj) { + $connection_obj->getADOdbObject()->$property = $value; + } + + return true; + } + + return false; + } + + /** + * Override the __clone() magic method. + */ + private function __clone() + { + } +} + +/** + * Class ADOdbLoadBalancerConnection + */ +class ADOdbLoadBalancerConnection +{ + /** + * @var bool ADOdb drive name. + */ + protected $driver = false; + + /** + * @var bool ADODB object. + */ + protected $adodb_obj = false; + + /** + * @var string Type of connection, either 'write' capable or 'readonly' + */ + public $type = 'write'; + + /** + * @var int Weight of connection, lower receives less queries, higher receives more queries. + */ + public $weight = 1; + + /** + * @var bool Determines if the connection persistent. + */ + public $persistent_connection = false; + + /** + * @var string Database connection host + */ + public $host = ''; + + /** + * @var string Database connection user + */ + public $user = ''; + + /** + * @var string Database connection password + */ + public $password = ''; + + /** + * @var string Database connection database name + */ + public $database = ''; + + /** + * ADOdbLoadBalancerConnection constructor to setup the ADODB object. + * + * @param $driver + * @param string $type + * @param int $weight + * @param bool $persistent_connection + * @param string $argHostname + * @param string $argUsername + * @param string $argPassword + * @param string $argDatabaseName + */ + public function __construct( + $driver, + $type = 'write', + $weight = 1, + $persistent_connection = false, + $argHostname = '', + $argUsername = '', + $argPassword = '', + $argDatabaseName = '' + ) { + if ($type !== 'write' && $type !== 'readonly') { + return false; + } + + $this->adodb_obj = ADONewConnection($driver); + + $this->type = $type; + $this->weight = $weight; + $this->persistent_connection = $persistent_connection; + + $this->host = $argHostname; + $this->user = $argUsername; + $this->password = $argPassword; + $this->database = $argDatabaseName; + + return true; + } + + /** + * Returns the ADODB object for this connection. + * + * @return bool + */ + public function getADOdbObject() + { + return $this->adodb_obj; + } +} diff --git a/adodb-memcache.lib.inc.php b/adodb-memcache.lib.inc.php index b603e56b..c871b970 100644 --- a/adodb-memcache.lib.inc.php +++ b/adodb-memcache.lib.inc.php @@ -28,11 +28,14 @@ $db->memCache = true; /// should we use memCache instead of caching in files $db->memCacheHost = array($ip1, $ip2, $ip3); $db->memCachePort = 11211; /// this is default memCache port $db->memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib) + /// Note; compression is not supported w/the memcached library $db->Connect(...); $db->CacheExecute($sql); - Note the memcache class is shared by all connections, is created during the first call to Connect/PConnect. + Notes; The memcache class is shared by all connections, is created during the first call to Connect/PConnect. + We'll look for both the memcache library (https://pecl.php.net/package/memcache) and the memcached + library (https://pecl.php.net/package/memcached). If both exist, the memcache library will be used. Class instance is stored in $ADODB_CACHE */ @@ -40,6 +43,11 @@ $db->CacheExecute($sql); class ADODB_Cache_MemCache { var $createdir = false; // create caching directory structure? + // $library will be populated with the proper library on connect + // and is used later when there are differences in specific calls + // between memcache and memcached + var $library = false; + //----------------------------- // memcache specific variables @@ -60,18 +68,23 @@ $db->CacheExecute($sql); // implement as lazy connection. The connection only occurs on CacheExecute call function connect(&$err) { - if (!function_exists('memcache_pconnect')) { - $err = 'Memcache module PECL extension not found!'; + // do we have memcache or memcached? + if (class_exists('Memcache')) { + $this->library='Memcache'; + $memcache = new MemCache; + } elseif (class_exists('Memcached')) { + $this->library='Memcached'; + $memcache = new MemCached; + } else { + $err = 'Neither the Memcache nor Memcached PECL extensions were found!'; return false; } - $memcache = new MemCache; - if (!is_array($this->hosts)) $this->hosts = array($this->hosts); $failcnt = 0; foreach($this->hosts as $host) { - if (!@$memcache->addServer($host,$this->port,true)) { + if (!@$memcache->addServer($host,$this->port)) { $failcnt += 1; } } @@ -93,8 +106,25 @@ $db->CacheExecute($sql); } if (!$this->_memcache) return false; - if (!$this->_memcache->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, $secs2cache)) { - if ($debug) ADOConnection::outp(" Failed to save data at the memcached server!<br>\n"); + $failed=false; + switch ($this->library) { + case 'Memcache': + if (!$this->_memcache->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, $secs2cache)) { + $failed=true; + } + break; + case 'Memcached': + if (!$this->_memcache->set($filename, $contents, $secs2cache)) { + $failed=true; + } + break; + default: + $failed=true; + break; + } + + if($failed) { + if ($debug) ADOConnection::outp(" Failed to save data at the memcache server!<br>\n"); return false; } @@ -110,7 +140,7 @@ $db->CacheExecute($sql); $rs = $this->_memcache->get($filename); if (!$rs) { - $err = 'Item with such key doesn\'t exists on the memcached server.'; + $err = 'Item with such key doesn\'t exist on the memcache server.'; return $false; } @@ -176,8 +206,8 @@ $db->CacheExecute($sql); $del = $this->_memcache->delete($filename); if ($debug) - if (!$del) ADOConnection::outp("flushcache: $key entry doesn't exist on memcached server!<br>\n"); - else ADOConnection::outp("flushcache: $key entry flushed from memcached server!<br>\n"); + if (!$del) ADOConnection::outp("flushcache: $key entry doesn't exist on memcache server!<br>\n"); + else ADOConnection::outp("flushcache: $key entry flushed from memcache server!<br>\n"); return $del; } diff --git a/adodb-pager.inc.php b/adodb-pager.inc.php index b1b05f0f..e2e8737a 100644 --- a/adodb-pager.inc.php +++ b/adodb-pager.inc.php @@ -275,7 +275,7 @@ class ADODB_Pager { } //------------------------------------------------------ - // override this to control overall layout and formating + // override this to control overall layout and formatting function RenderLayout($header,$grid,$footer,$attributes='border=1 bgcolor=beige') { echo "<table ".$attributes."><tr><td>", diff --git a/adodb-pear.inc.php b/adodb-pear.inc.php index 96de6e88..f0e5702c 100644 --- a/adodb-pear.inc.php +++ b/adodb-pear.inc.php @@ -336,7 +336,7 @@ class DB $parsed['hostspec'] = urldecode($str); } - // Get dabase if any + // Get database if any // $dsn => database if (!empty($dsn)) { $parsed['database'] = $dsn; diff --git a/adodb-perf.inc.php b/adodb-perf.inc.php index 64947008..a7c3f2df 100644 --- a/adodb-perf.inc.php +++ b/adodb-perf.inc.php @@ -170,6 +170,9 @@ function adodb_log_sql(&$connx,$sql,$inputarr) mysqli_free_result($conn->_queryID); } $ok = $conn->Execute($isql,$arr); + if($conn instanceof ADODB_mysqli && $conn->_queryID){ + mysqli_free_result($conn->_queryID); + } } else $ok = true; @@ -263,12 +266,6 @@ processes 69293 // Algorithm is taken from // http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/414b0e1b-499c-411e-8a02-6a12e339c0f1/ if (strncmp(PHP_OS,'WIN',3)==0) { - if (PHP_VERSION == '5.0.0') return false; - if (PHP_VERSION == '5.0.1') return false; - if (PHP_VERSION == '5.0.2') return false; - if (PHP_VERSION == '5.0.3') return false; - if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737 - static $FAIL = false; if ($FAIL) return false; @@ -593,7 +590,7 @@ Committed_AS: 348732 kB } /* - Raw function returning array of poll paramters + Raw function returning array of poll parameters */ function PollParameters() { diff --git a/adodb-time.inc.php b/adodb-time.inc.php index d36f7769..4e2531c3 100644 --- a/adodb-time.inc.php +++ b/adodb-time.inc.php @@ -38,7 +38,7 @@ of date()'s field formats. Mktime() will convert from local time to GMT, and date() will convert from GMT to local time, but daylight savings is not handled currently. -This library is independant of the rest of ADOdb, and can be used +This library is independent of the rest of ADOdb, and can be used as standalone code. PERFORMANCE @@ -268,7 +268,7 @@ Changed adodb_get_gm_diff to use DateTimeZone(). * Now adodb_mktime(0,0,0,24,1,2037) works correctly. - 15 July 2007 0.30 -Added PHP 5.2.0 compatability fixes. +Added PHP 5.2.0 compatibility fixes. * gmtime behaviour for 1970 has changed. We use the actual date if it is between 1970 to 2038 to get the * timezone, otherwise we use the current year as the baseline to retrieve the timezone. * Also the timezone's in php 5.2.* support historical data better, eg. if timezone today was +8, but @@ -404,8 +404,6 @@ First implementation. */ define('ADODB_DATE_VERSION',0.35); -$ADODB_DATETIME_CLASS = (PHP_VERSION >= 5.2); - /* This code was originally for windows. But apparently this problem happens also with Linux, RH 7.3 and later! @@ -532,8 +530,8 @@ function adodb_date_test() if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>"; if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>"; - // Test string formating - print "<p>Testing date formating</p>"; + // Test string formatting + print "<p>Testing date formatting</p>"; $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C2822 r s t U w y Y z Z 2003'; $s1 = date($fmt,0); @@ -737,13 +735,12 @@ function adodb_get_gmt_diff_ts($ts) */ function adodb_get_gmt_diff($y,$m,$d) { -static $TZ,$tzo; -global $ADODB_DATETIME_CLASS; + static $TZ,$tzo; if (!defined('ADODB_TEST_DATES')) $y = false; else if ($y < 1970 || $y >= 2038) $y = false; - if ($ADODB_DATETIME_CLASS && $y !== false) { + if ($y !== false) { $dt = new DateTime(); $dt->setISODate($y,$m,$d); if (empty($tzo)) { @@ -1035,20 +1032,20 @@ global $_month_table_normal,$_month_table_leaf, $_adodb_last_date_call_failed; 0 => $origd ); } -/* - if ($isphp5) - $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); - else - $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); - break;*/ -function adodb_tz_offset($gmt,$isphp5) + +/** + * Compute timezone offset. + * + * @param int $gmt Time offset from GMT, in seconds + * @param bool $ignored Param leftover from removed PHP4-compatibility code + * kept to avoid altering function signature. + * @return string + */ +function adodb_tz_offset($gmt, $ignored=true) { - $zhrs = abs($gmt)/3600; + $zhrs = abs($gmt) / 3600; $hrs = floor($zhrs); - if ($isphp5) - return sprintf('%s%02d%02d',($gmt<=0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60); - else - return sprintf('%s%02d%02d',($gmt<0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60); + return sprintf('%s%02d%02d', ($gmt <= 0) ? '+' : '-', $hrs, ($zhrs - $hrs) * 60); } @@ -1081,10 +1078,8 @@ function adodb_date2($fmt, $d=false, $is_gmt=false) */ function adodb_date($fmt,$d=false,$is_gmt=false) { -static $daylight; -global $ADODB_DATETIME_CLASS; -static $jan1_1971; - + static $daylight; + static $jan1_1971; if (!isset($daylight)) { $daylight = function_exists('adodb_daylight_sv'); @@ -1093,7 +1088,15 @@ static $jan1_1971; if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt); if (!defined('ADODB_TEST_DATES')) { - if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range + + /* + * Format 'Q' is an ADOdb custom format, not supported in PHP + * so if there is a 'Q' in the format, we force it to use our + * function. There is a trivial overhead in this + */ + + if ((abs($d) <= 0x7FFFFFFF) && strpos($fmt,'Q') === false) + { // check if number in 32-bit signed range if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= $jan1_1971) // if windows, must be +ve integer return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d); @@ -1116,8 +1119,6 @@ static $jan1_1971; $max = strlen($fmt); $dates = ''; - $isphp5 = PHP_VERSION >= 5; - /* at this point, we have the following integer vars to manipulate: $year, $month, $day, $hour, $min, $secs @@ -1128,12 +1129,9 @@ static $jan1_1971; $dates .= date('e'); break; case 'T': - if ($ADODB_DATETIME_CLASS) { - $dt = new DateTime(); - $dt->SetDate($year,$month,$day); - $dates .= $dt->Format('T'); - } else - $dates .= date('T'); + $dt = new DateTime(); + $dt->SetDate($year,$month,$day); + $dates .= $dt->Format('T'); break; // YEAR case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; @@ -1152,14 +1150,16 @@ static $jan1_1971; $gmt = adodb_get_gmt_diff($year,$month,$day); - $dates .= ' '.adodb_tz_offset($gmt,$isphp5); + $dates .= ' '.adodb_tz_offset($gmt); break; case 'Y': $dates .= $year; break; case 'y': $dates .= substr($year,strlen($year)-2,2); break; // MONTH case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; - case 'Q': $dates .= ($month+3)>>2; break; + case 'Q': + $dates .= ceil($month / 3); + break; case 'n': $dates .= $month; break; case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; @@ -1167,6 +1167,9 @@ static $jan1_1971; case 't': $dates .= $arr['ndays']; break; case 'z': $dates .= $arr['yday']; break; case 'w': $dates .= adodb_dow($year,$month,$day); break; + case 'W': + $dates .= sprintf('%02d',ceil( $arr['yday'] / 7) - 1); + break; case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break; case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break; case 'j': $dates .= $day; break; @@ -1185,7 +1188,7 @@ static $jan1_1971; case 'O': $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff($year,$month,$day); - $dates .= adodb_tz_offset($gmt,$isphp5); + $dates .= adodb_tz_offset($gmt); break; case 'H': diff --git a/adodb-xmlschema03.inc.php b/adodb-xmlschema03.inc.php index 4d1faad3..2ea49221 100644 --- a/adodb-xmlschema03.inc.php +++ b/adodb-xmlschema03.inc.php @@ -204,7 +204,7 @@ class dbObject { * @param string $field Field. * @return string Field ID. */ - function FieldID( $field ) { + function fieldID( $field ) { return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) ); } } @@ -347,6 +347,14 @@ class dbTable extends dbObject { */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { + // Table or field comment + case 'DESCR': + if( isset( $this->current_field ) ) { + $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata ); + } else { + $this->addTableComment( $cdata ); + } + break; // Table/field constraint case 'CONSTRAINT': if( isset( $this->current_field ) ) { @@ -449,7 +457,7 @@ class dbTable extends dbObject { * @return array Field specifier array */ function addField( $name, $type, $size = NULL, $opts = NULL ) { - $field_id = $this->FieldID( $name ); + $field_id = $this->fieldID( $name ); // Set the field index so we know where we are $this->current_field = $field_id; @@ -506,11 +514,15 @@ class dbTable extends dbObject { */ function addTableOpt( $opt ) { if(isset($this->currentPlatform)) { - $this->opts[$this->parent->db->databaseType] = $opt; + $this->opts[$this->parent->db->dataProvider] = $opt; } return $this->opts; } + function addTableComment( $opt ) { + $this->opts['comment'] = $opt; + return $this->opts; + } /** * Generates the SQL that will create the table in the database @@ -522,9 +534,9 @@ class dbTable extends dbObject { $sql = array(); // drop any existing indexes - if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) { + if( is_array( $legacy_indexes = $xmls->dict->metaIndexes( $this->name ) ) ) { foreach( $legacy_indexes as $index => $index_details ) { - $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name ); + $sql[] = $xmls->dict->dropIndexSQL( $index, $this->name ); } } @@ -534,10 +546,10 @@ class dbTable extends dbObject { } // if table exists - if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) { + if( is_array( $legacy_fields = $xmls->dict->metaColumns( $this->name ) ) ) { // drop table if( $this->drop_table ) { - $sql[] = $xmls->dict->DropTableSQL( $this->name ); + $sql[] = $xmls->dict->dropTableSQL( $this->name ); return $sql; } @@ -545,7 +557,7 @@ class dbTable extends dbObject { // drop any existing fields not in schema foreach( $legacy_fields as $field_id => $field ) { if( !isset( $this->fields[$field_id] ) ) { - $sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name ); + $sql[] = $xmls->dict->dropColumnSQL( $this->name, $field->name ); } } // if table doesn't exist @@ -591,21 +603,21 @@ class dbTable extends dbObject { if( empty( $legacy_fields ) ) { // Create the new table - $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); - logMsg( end( $sql ), 'Generated CreateTableSQL' ); + $sql[] = $xmls->dict->createTableSQL( $this->name, $fldarray, $this->opts ); + logMsg( end( $sql ), 'Generated createTableSQL' ); } else { // Upgrade an existing table logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" ); switch( $xmls->upgrade ) { // Use ChangeTableSQL case 'ALTER': - logMsg( 'Generated ChangeTableSQL (ALTERing table)' ); - $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts ); + logMsg( 'Generated changeTableSQL (ALTERing table)' ); + $sql[] = $xmls->dict->changeTableSQL( $this->name, $fldarray, $this->opts ); break; case 'REPLACE': logMsg( 'Doing upgrade REPLACE (testing)' ); - $sql[] = $xmls->dict->DropTableSQL( $this->name ); - $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); + $sql[] = $xmls->dict->dropTableSQL( $this->name ); + $sql[] = $xmls->dict->createTableSQL( $this->name, $fldarray, $this->opts ); break; // ignore table default: @@ -758,7 +770,7 @@ class dbIndex extends dbObject { * @return string Field list */ function addField( $name ) { - $this->columns[$this->FieldID( $name )] = $name; + $this->columns[$this->fieldID( $name )] = $name; // Return the field list return $this->columns; @@ -795,7 +807,7 @@ class dbIndex extends dbObject { } } - return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts ); + return $xmls->dict->createIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts ); } /** @@ -903,7 +915,7 @@ class dbData extends dbObject { // Set the field index so we know where we are if( isset( $attributes['NAME'] ) ) { - $this->current_field = $this->FieldID( $attributes['NAME'] ); + $this->current_field = $this->fieldID( $attributes['NAME'] ); } else { $this->current_field = count( $this->data[$this->row] ); } @@ -935,12 +947,12 @@ class dbData extends dbObject { * @return array Array containing index creation SQL */ function create( &$xmls ) { - $table = $xmls->dict->TableName($this->parent->name); + $table = $xmls->dict->tableName($this->parent->name); $table_field_count = count($this->parent->fields); - $tables = $xmls->db->MetaTables(); + $tables = $xmls->db->metaTables(); $sql = array(); - $ukeys = $xmls->db->MetaPrimaryKeys( $table ); + $ukeys = $xmls->db->metaPrimaryKeys( $table ); if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) { foreach( $this->parent->indexes as $indexObj ) { if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name; @@ -1027,19 +1039,19 @@ class dbData extends dbObject { $where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] ); } } - $records = $xmls->db->Execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where ); - switch( $records->RecordCount() ) { + $records = $xmls->db->execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where ); + switch( $records->recordCount() ) { case 0: // No matching record, so safe to insert. logMsg( "No matching records. Inserting new row with unique data" ); - $sql[] = $xmls->db->GetInsertSQL( $records, $mfields ); + $sql[] = $xmls->db->getInsertSQL( $records, $mfields ); break; case 1: // Exactly one matching record, so we can update if the mode permits. logMsg( "One matching record..." ); if( $mode == XMLS_MODE_UPDATE ) { logMsg( "...Updating existing row from unique data" ); - $sql[] = $xmls->db->GetUpdateSQL( $records, $mfields ); + $sql[] = $xmls->db->getUpdateSQL( $records, $mfields ); } break; default: @@ -1440,7 +1452,7 @@ class adoSchema { * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE) * @returns string Upgrade method used */ - function SetUpgradeMethod( $method = '' ) { + function setUpgradeMethod( $method = '' ) { if( !is_string( $method ) ) { return FALSE; } @@ -1488,7 +1500,7 @@ class adoSchema { * @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE * @return int current mode */ - function ExistingData( $mode = NULL ) { + function existingData( $mode = NULL ) { if( is_int( $mode ) ) { switch( $mode ) { case XMLS_MODE_UPDATE: @@ -1523,7 +1535,7 @@ class adoSchema { * * @see ParseSchema(), ExecuteSchema() */ - function ExecuteInline( $mode = NULL ) { + function executeInline( $mode = NULL ) { if( is_bool( $mode ) ) { $this->executeInline = $mode; } @@ -1544,7 +1556,7 @@ class adoSchema { * * @see addSQL(), ExecuteSchema() */ - function ContinueOnError( $mode = NULL ) { + function continueOnError( $mode = NULL ) { if( is_bool( $mode ) ) { $this->continueOnError = $mode; } @@ -1565,8 +1577,8 @@ class adoSchema { * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute */ - function ParseSchema( $filename, $returnSchema = FALSE ) { - return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); + function parseSchema( $filename, $returnSchema = FALSE ) { + return $this->parseSchemaString( $this->convertSchemaFile( $filename ), $returnSchema ); } /** @@ -1575,22 +1587,22 @@ class adoSchema { * Call this method to load the specified schema directly from a file (see * the DTD for the proper format) and generate the SQL necessary to create * the database described by the schema. Use this method when you are dealing - * with large schema files. Otherwise, ParseSchema() is faster. + * with large schema files. Otherwise, parseSchema() is faster. * This method does not automatically convert the schema to the latest axmls * schema version. You must convert the schema manually using either the - * ConvertSchemaFile() or ConvertSchemaString() method. - * @see ParseSchema() - * @see ConvertSchemaFile() - * @see ConvertSchemaString() + * convertSchemaFile() or convertSchemaString() method. + * @see parseSchema() + * @see convertSchemaFile() + * @see convertSchemaString() * * @param string $file Name of XML schema file. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. * - * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString() - * @see ParseSchema(), ParseSchemaString() + * @deprecated Replaced by adoSchema::parseSchema() and adoSchema::parseSchemaString() + * @see parseSchema(), parseSchemaString() */ - function ParseSchemaFile( $filename, $returnSchema = FALSE ) { + function parseSchemaFile( $filename, $returnSchema = FALSE ) { // Open the file if( !($fp = fopen( $filename, 'r' )) ) { logMsg( 'Unable to open file' ); @@ -1598,7 +1610,7 @@ class adoSchema { } // do version detection here - if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) { + if( $this->schemaFileVersion( $filename ) != $this->schemaVersion ) { logMsg( 'Invalid Schema Version' ); return FALSE; } @@ -1636,13 +1648,13 @@ class adoSchema { * * Call this method to parse a string containing an XML schema (see the DTD for the proper format) * and generate the SQL necessary to create the database described by the schema. - * @see ParseSchema() + * @see parseSchema() * * @param string $xmlstring XML schema string. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. */ - function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) { + function parseSchemaString( $xmlstring, $returnSchema = FALSE ) { if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { logMsg( 'Empty or Invalid Schema' ); return FALSE; @@ -1686,8 +1698,8 @@ class adoSchema { * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute */ - function RemoveSchema( $filename, $returnSchema = FALSE ) { - return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); + function removeSchema( $filename, $returnSchema = FALSE ) { + return $this->removeSchemaString( $this->convertSchemaFile( $filename ), $returnSchema ); } /** @@ -1695,38 +1707,38 @@ class adoSchema { * * Call this method to parse a string containing an XML schema (see the DTD for the proper format) * and generate the SQL necessary to uninstall the database described by the schema. - * @see RemoveSchema() + * @see removeSchema() * * @param string $schema XML schema string. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. */ - function RemoveSchemaString( $schema, $returnSchema = FALSE ) { + function removeSchemaString( $schema, $returnSchema = FALSE ) { // grab current version - if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { + if( !( $version = $this->schemaStringVersion( $schema ) ) ) { return FALSE; } - return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema ); + return $this->parseSchemaString( $this->transformSchema( $schema, 'remove-' . $version), $returnSchema ); } /** * Applies the current XML schema to the database (post execution). * * Call this method to apply the current schema (generally created by calling - * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, + * parseSchema() or parseSchemaString() ) to the database (creating the tables, indexes, * and executing other SQL specified in the schema) after parsing. - * @see ParseSchema(), ParseSchemaString(), ExecuteInline() + * @see parseSchema(), parseSchemaString(), executeInline() * * @param array $sqlArray Array of SQL statements that will be applied rather than * the current schema. * @param boolean $continueOnErr Continue to apply the schema even if an error occurs. * @returns integer 0 if failure, 1 if errors, 2 if successful. */ - function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) { + function executeSchema( $sqlArray = NULL, $continueOnErr = NULL ) { if( !is_bool( $continueOnErr ) ) { - $continueOnErr = $this->ContinueOnError(); + $continueOnErr = $this->continueOnError(); } if( !isset( $sqlArray ) ) { @@ -1736,7 +1748,7 @@ class adoSchema { if( !is_array( $sqlArray ) ) { $this->success = 0; } else { - $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); + $this->success = $this->dict->executeSQLArray( $sqlArray, $continueOnErr ); } return $this->success; @@ -1746,12 +1758,12 @@ class adoSchema { * Returns the current SQL array. * * Call this method to fetch the array of SQL queries resulting from - * ParseSchema() or ParseSchemaString(). + * parseSchema() or parseSchemaString(). * * @param string $format Format: HTML, TEXT, or NONE (PHP array) * @return array Array of SQL statements or FALSE if an error occurs */ - function PrintSQL( $format = 'NONE' ) { + function printSQL( $format = 'NONE' ) { $sqlArray = null; return $this->getSQL( $format, $sqlArray ); } @@ -1765,7 +1777,7 @@ class adoSchema { * @param string $filename Path and name where the file should be saved. * @return boolean TRUE if save is successful, else FALSE. */ - function SaveSQL( $filename = './schema.sql' ) { + function saveSQL( $filename = './schema.sql' ) { if( !isset( $sqlArray ) ) { $sqlArray = $this->sqlArray; @@ -1853,17 +1865,17 @@ class adoSchema { * parameter is specified, the schema will be converted to the current DTD version. * If the newFile parameter is provided, the converted schema will be written to the specified * file. - * @see ConvertSchemaFile() + * @see convertSchemaFile() * * @param string $schema String containing XML schema that will be converted. * @param string $newVersion DTD version to convert to. * @param string $newFile File name of (converted) output file. * @return string Converted XML schema or FALSE if an error occurs. */ - function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) { + function convertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) { // grab current version - if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { + if( !( $version = $this->schemaStringVersion( $schema ) ) ) { return FALSE; } @@ -1874,7 +1886,7 @@ class adoSchema { if( $version == $newVersion ) { $result = $schema; } else { - $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion); + $result = $this->transformSchema( $schema, 'convert-' . $version . '-' . $newVersion); } if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { @@ -1902,17 +1914,17 @@ class adoSchema { * parameter is specified, the schema will be converted to the current DTD version. * If the newFile parameter is provided, the converted schema will be written to the specified * file. - * @see ConvertSchemaString() + * @see convertSchemaString() * * @param string $filename Name of XML schema file that will be converted. * @param string $newVersion DTD version to convert to. * @param string $newFile File name of (converted) output file. * @return string Converted XML schema or FALSE if an error occurs. */ - function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) { + function convertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) { // grab current version - if( !( $version = $this->SchemaFileVersion( $filename ) ) ) { + if( !( $version = $this->schemaFileVersion( $filename ) ) ) { return FALSE; } @@ -1928,7 +1940,7 @@ class adoSchema { $result = substr( $result, 3 ); } } else { - $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' ); + $result = $this->transformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' ); } if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { @@ -1939,7 +1951,7 @@ class adoSchema { return $result; } - function TransformSchema( $schema, $xsl, $schematype='string' ) + function transformSchema( $schema, $xsl, $schematype='string' ) { // Fail if XSLT extension is not available if( ! function_exists( 'xslt_create' ) ) { @@ -2050,7 +2062,7 @@ class adoSchema { * @param string $filename AXMLS schema file * @return string Schema version number or FALSE on error */ - function SchemaFileVersion( $filename ) { + function schemaFileVersion( $filename ) { // Open the file if( !($fp = fopen( $filename, 'r' )) ) { // die( 'Unable to open file' ); @@ -2076,7 +2088,7 @@ class adoSchema { * @param string $xmlstring XML schema string * @return string Schema version number or FALSE on error */ - function SchemaStringVersion( $xmlstring ) { + function schemaStringVersion( $xmlstring ) { if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { return FALSE; } @@ -2101,12 +2113,12 @@ class adoSchema { * @stripprefix strip prefix string when storing in XML schema * @return string Generated XML schema */ - function ExtractSchema( $data = FALSE, $indent = ' ', $prefix = '' , $stripprefix=false) { - $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM ); + function extractSchema( $data = FALSE, $indent = ' ', $prefix = '' , $stripprefix=false) { + $old_mode = $this->db->setFetchMode( ADODB_FETCH_NUM ); $schema = '<?xml version="1.0"?>' . "\n" . '<schema version="' . $this->schemaVersion . '">' . "\n"; - if( is_array( $tables = $this->db->MetaTables( 'TABLES' ,false ,($prefix) ? str_replace('_','\_',$prefix).'%' : '') ) ) { + if( is_array( $tables = $this->db->metaTables( 'TABLES' ,false ,($prefix) ? str_replace('_','\_',$prefix).'%' : '') ) ) { foreach( $tables as $table ) { $schema .= $indent . '<table name="' @@ -2114,9 +2126,9 @@ class adoSchema { . '">' . "\n"; // grab details from database - $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE -1' ); - $fields = $this->db->MetaColumns( $table ); - $indexes = $this->db->MetaIndexes( $table ); + $rs = $this->db->execute( 'SELECT * FROM ' . $table . ' WHERE -1' ); + $fields = $this->db->metaColumns( $table ); + $indexes = $this->db->metaIndexes( $table ); if( is_array( $fields ) ) { foreach( $fields as $details ) { @@ -2148,7 +2160,7 @@ class adoSchema { // this stops the creation of 'R' columns, // AUTOINCREMENT is used to create auto columns $details->primary_key = 0; - $type = $rs->MetaType( $details ); + $type = $rs->metaType( $details ); $schema .= str_repeat( $indent, 2 ) . '<field name="' . htmlentities( $details->name ) . '" type="' . $type . '"' . $extra; @@ -2179,12 +2191,12 @@ class adoSchema { } if( $data ) { - $rs = $this->db->Execute( 'SELECT * FROM ' . $table ); + $rs = $this->db->execute( 'SELECT * FROM ' . $table ); if( is_object( $rs ) && !$rs->EOF ) { $schema .= str_repeat( $indent, 2 ) . "<data>\n"; - while( $row = $rs->FetchRow() ) { + while( $row = $rs->fetchRow() ) { foreach( $row as $key => $val ) { if ( $val != htmlentities( $val ) ) { $row[$key] = '<![CDATA[' . $val . ']]>'; @@ -2202,7 +2214,7 @@ class adoSchema { } } - $this->db->SetFetchMode( $old_mode ); + $this->db->setFetchMode( $old_mode ); $schema .= '</schema>'; return $schema; @@ -2218,7 +2230,7 @@ class adoSchema { * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix. * @return boolean TRUE if successful, else FALSE */ - function SetPrefix( $prefix = '', $underscore = TRUE ) { + function setPrefix( $prefix = '', $underscore = TRUE ) { switch( TRUE ) { // clear prefix case empty( $prefix ): @@ -2377,7 +2389,7 @@ class adoSchema { * Call this method to clean up after an adoSchema object that is no longer in use. * @deprecated adoSchema now cleans up automatically. */ - function Destroy() { + function destroy() { if ($this->mgq !== false) { ini_set('magic_quotes_runtime', $this->mgq ); } diff --git a/adodb.inc.php b/adodb.inc.php index da3f7138..4ae8cad7 100644 --- a/adodb.inc.php +++ b/adodb.inc.php @@ -72,7 +72,6 @@ if (!defined('_ADODB_LAYER')) { $ADODB_CACHE_DIR, // directory to cache recordsets $ADODB_CACHE, $ADODB_CACHE_CLASS, - $ADODB_EXTENSION, // ADODB extension installed $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF $ADODB_FETCH_MODE, // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default... $ADODB_GETONE_EOF, @@ -82,35 +81,52 @@ if (!defined('_ADODB_LAYER')) { // GLOBAL SETUP //============================================================================================== - $ADODB_EXTENSION = defined('ADODB_EXTENSION'); - - // ******************************************************** - // Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3). - // Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi - // - // 0 = ignore empty fields. All empty fields in array are ignored. - // 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values. - // 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values. - // 3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values. - + /********************************************************* + * Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3). + * Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi + * @link http://adodb.org/dokuwiki/doku.php?id=v5:reference:adodb_force_type + * + * 0 = ignore empty fields. All empty fields in array are ignored. + * 1 = force null. All empty, php null and string 'null' fields are + * changed to sql NULL values. + * 2 = force empty. All empty, php null and string 'null' fields are + * changed to sql empty '' or 0 values. + * 3 = force value. Value is left as it is. Php null and string 'null' + * are set to sql NULL values and empty fields '' are set to empty '' sql values. + * 4 = force value. Like 1 but numeric empty fields are set to zero. + */ define('ADODB_FORCE_IGNORE',0); define('ADODB_FORCE_NULL',1); define('ADODB_FORCE_EMPTY',2); define('ADODB_FORCE_VALUE',3); + define('ADODB_FORCE_NULL_AND_ZERO',4); // ******************************************************** - if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) { + /** + * Constants for returned values from the charMax and textMax methods. + * If not specifically defined in the driver, methods return the NOTSET value. + */ + define ('ADODB_STRINGMAX_NOTSET', -1); + define ('ADODB_STRINGMAX_NOLIMIT',-2); + + /* + * Defines the the default meta type returned + * when ADOdb encounters a type that it is not + * defined in the metaTypes. + */ + if (!defined('ADODB_DEFAULT_METATYPE')) + define ('ADODB_DEFAULT_METATYPE','N'); - define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>'); + define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>'); // allow [ ] @ ` " and . in table names - define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)'); + define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)'); // prefetching used by oracle - if (!defined('ADODB_PREFETCH_ROWS')) { - define('ADODB_PREFETCH_ROWS',10); - } + if (!defined('ADODB_PREFETCH_ROWS')) { + define('ADODB_PREFETCH_ROWS',10); + } /** @@ -125,10 +141,10 @@ if (!defined('_ADODB_LAYER')) { * - BOTH: array(0 => 456, 'id' => 456, 1 => 'john', 'name' => 'john') * - DEFAULT: driver-dependent */ - define('ADODB_FETCH_DEFAULT', 0); - define('ADODB_FETCH_NUM', 1); - define('ADODB_FETCH_ASSOC', 2); - define('ADODB_FETCH_BOTH', 3); + define('ADODB_FETCH_DEFAULT', 0); + define('ADODB_FETCH_NUM', 1); + define('ADODB_FETCH_ASSOC', 2); + define('ADODB_FETCH_BOTH', 3); /** * Associative array case constants @@ -145,54 +161,24 @@ if (!defined('_ADODB_LAYER')) { * NOTE: This functionality is not implemented everywhere, it currently * works only with: mssql, odbc, oci8 and ibase derived drivers */ - define('ADODB_ASSOC_CASE_LOWER', 0); - define('ADODB_ASSOC_CASE_UPPER', 1); - define('ADODB_ASSOC_CASE_NATIVE', 2); - - - if (!defined('TIMESTAMP_FIRST_YEAR')) { - define('TIMESTAMP_FIRST_YEAR',100); - } - - /** - * AutoExecute constants - * (moved from adodb-pear.inc.php since they are only used in here) - */ - define('DB_AUTOQUERY_INSERT', 1); - define('DB_AUTOQUERY_UPDATE', 2); + define('ADODB_ASSOC_CASE_LOWER', 0); + define('ADODB_ASSOC_CASE_UPPER', 1); + define('ADODB_ASSOC_CASE_NATIVE', 2); - // PHP's version scheme makes converting to numbers difficult - workaround - $_adodb_ver = (float) PHP_VERSION; - if ($_adodb_ver >= 5.2) { - define('ADODB_PHPVER',0x5200); - } else if ($_adodb_ver >= 5.0) { - define('ADODB_PHPVER',0x5000); - } else { - die("PHP5 or later required. You are running ".PHP_VERSION); - } - unset($_adodb_ver); + if (!defined('TIMESTAMP_FIRST_YEAR')) { + define('TIMESTAMP_FIRST_YEAR',100); } - /** - Accepts $src and $dest arrays, replacing string $data - */ - function ADODB_str_replace($src, $dest, $data) { - if (ADODB_PHPVER >= 0x4050) { - return str_replace($src,$dest,$data); - } + * AutoExecute constants + * (moved from adodb-pear.inc.php since they are only used in here) + */ + define('DB_AUTOQUERY_INSERT', 1); + define('DB_AUTOQUERY_UPDATE', 2); - $s = reset($src); - $d = reset($dest); - while ($s !== false) { - $data = str_replace($s,$d,$data); - $s = next($src); - $d = next($dest); - } - return $data; - } + function ADODB_Setup() { GLOBAL $ADODB_vers, // database version @@ -439,6 +425,8 @@ if (!defined('_ADODB_LAYER')) { var $false = '0'; /// string that represents FALSE for a database var $replaceQuote = "\\'"; /// string to use to replace quotes var $nameQuote = '"'; /// string to use to quote identifiers and names + var $leftBracket = '['; /// left square bracked for t-sql styled column names + var $rightBracket = ']'; /// right square bracked for t-sql styled column names var $charSet=false; /// character set to use - only for interbase, postgres and oci8 var $metaDatabasesSQL = ''; var $metaTablesSQL = ''; @@ -457,6 +445,7 @@ if (!defined('_ADODB_LAYER')) { var $hasTransactions = true; /// has transactions //-- var $genID = 0; /// sequence id used by GenID(); + /** @var bool|callable */ var $raiseErrorFn = false; /// error function to call var $isoDates = false; /// accepts dates in ISO format var $cacheSecs = 3600; /// cache for 1 hour @@ -465,7 +454,7 @@ if (!defined('_ADODB_LAYER')) { var $memCache = false; /// should we use memCache instead of caching in files var $memCacheHost; /// memCache host var $memCachePort = 11211; /// memCache port - var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib) + var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib, not supported w/memcached library) var $sysDate = false; /// name of function that returns the current date var $sysTimeStamp = false; /// name of function that returns the current timestamp @@ -482,8 +471,9 @@ if (!defined('_ADODB_LAYER')) { var $ansiOuter = false; /// whether ansi outer join syntax supported var $autoRollback = false; // autoRollback on PConnect(). var $poorAffectedRows = false; // affectedRows not working or unreliable - + /** @var bool|callable */ var $fnExecute = false; + /** @var bool|callable */ var $fnCacheExecute = false; var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char var $rsPrefix = "ADORecordSet_"; @@ -514,6 +504,17 @@ if (!defined('_ADODB_LAYER')) { var $_logsql = false; var $_transmode = ''; // transaction mode + + /** + * Default Constructor. + * We define it even though it does not actually do anything. This avoids + * getting a PHP Fatal error: Cannot call constructor if a subclass tries + * to call its parent constructor. + */ + public function __construct() + { + } + /* * Additional parameters that may be passed to drivers in the connect string * Driver must be coded to accept the parameters @@ -533,13 +534,17 @@ if (!defined('_ADODB_LAYER')) { * * @example, for mssqlnative driver ('CharacterSet','UTF-8') */ - final public function setConnectionParameter($parameter,$value) - { + final public function setConnectionParameter($parameter,$value) { - $this->connectionParameters[$parameter] = $value; + $this->connectionParameters[] = array($parameter=>$value); } + /** + * ADOdb version. + * + * @return string + */ static function Version() { global $ADODB_vers; @@ -561,15 +566,20 @@ if (!defined('_ADODB_LAYER')) { } /** - Get server version info... - - @returns An array with 2 elements: $arr['string'] is the description string, - and $arr[version] is the version (also a string). - */ + * Get server version info. + * + * @return string[] An array with 2 elements: $arr['string'] is the description string, + * and $arr[version] is the version (also a string). + */ function ServerInfo() { return array('description' => '', 'version' => ''); } + /** + * Return true if connected to the database. + * + * @return bool + */ function IsConnected() { return !empty($this->_connectionID); } @@ -583,9 +593,13 @@ if (!defined('_ADODB_LAYER')) { } /** - * All error messages go through this bottleneck function. - * You can define your own handler by defining the function name in ADODB_OUTP. - */ + * All error messages go through this bottleneck function. + * + * You can define your own handler by defining the function name in ADODB_OUTP. + * + * @param string $msg Message to print + * @param bool $newline True to add a newline after printing $msg + */ static function outp($msg,$newline=true) { global $ADODB_FLUSH,$ADODB_OUTP; @@ -594,8 +608,7 @@ if (!defined('_ADODB_LAYER')) { $fn($msg,$newline); return; } else if (isset($ADODB_OUTP)) { - $fn = $ADODB_OUTP; - $fn($msg,$newline); + call_user_func($ADODB_OUTP,$msg,$newline); return; } @@ -616,6 +629,10 @@ if (!defined('_ADODB_LAYER')) { } + /** + * Return the database server's current date and time. + * @return int|false + */ function Time() { $rs = $this->_Execute("select $this->sysTimeStamp"); if ($rs && !$rs->EOF) { @@ -646,15 +663,15 @@ if (!defined('_ADODB_LAYER')) { } /** - * Connect to database + * Connect to database. * - * @param [argHostname] Host to connect to - * @param [argUsername] Userid to login - * @param [argPassword] Associated password - * @param [argDatabaseName] database - * @param [forceNew] force new connection + * @param string $argHostname Host to connect to + * @param string $argUsername Userid to login + * @param string $argPassword Associated password + * @param string $argDatabaseName Database name + * @param bool $forceNew Force new connection * - * @return true or false + * @return bool */ function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) { if ($argHostname != "") { @@ -709,30 +726,31 @@ if (!defined('_ADODB_LAYER')) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName); } - /** - * Always force a new connection to database - currently only works with oracle + * Always force a new connection to database. + * + * Currently this only works with Oracle. * - * @param [argHostname] Host to connect to - * @param [argUsername] Userid to login - * @param [argPassword] Associated password - * @param [argDatabaseName] database + * @param string $argHostname Host to connect to + * @param string $argUsername Userid to login + * @param string $argPassword Associated password + * @param string $argDatabaseName Database name * - * @return true or false + * @return bool */ function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") { return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true); } /** - * Establish persistent connect to database + * Establish persistent connection to database. * - * @param [argHostname] Host to connect to - * @param [argUsername] Userid to login - * @param [argPassword] Associated password - * @param [argDatabaseName] database + * @param string $argHostname Host to connect to + * @param string $argUsername Userid to login + * @param string $argPassword Associated password + * @param string $argDatabaseName Database name * - * @return return true or false + * @return bool */ function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") { @@ -769,7 +787,7 @@ if (!defined('_ADODB_LAYER')) { $ret = false; } else { $err = "Missing extension for ".$this->dataProvider; - $ret = 0; + $ret = false; } if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this); @@ -790,7 +808,11 @@ if (!defined('_ADODB_LAYER')) { ADOConnection::outp($msg); } - // create cache class. Code is backward compat with old memcache implementation + /** + * Create cache class. + * + * Code is backwards-compatible with old memcache implementation. + */ function _CreateCache() { global $ADODB_CACHE, $ADODB_CACHE_CLASS; @@ -806,8 +828,16 @@ if (!defined('_ADODB_LAYER')) { } } - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) { + /** + * Format date column in sql string. + * + * See https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:sqldate + * for documentation on supported formats. + * + * @param string $fmt Format string + * @param string $col Date column; use system date if not specified. + */ + function SQLDate($fmt, $col = '') { if (!$col) { $col = $this->sysDate; } @@ -815,7 +845,8 @@ if (!defined('_ADODB_LAYER')) { } /** - * Should prepare the sql statement and return the stmt resource. + * Prepare an sql statement and return the statement resource. + * * For databases that do not support this, we return the $sql. To ensure * compatibility with databases that do not support prepare: * @@ -823,29 +854,28 @@ if (!defined('_ADODB_LAYER')) { * $db->Execute($stmt,array(1,'Jill')) or die('insert failed'); * $db->Execute($stmt,array(2,'Joe')) or die('insert failed'); * - * @param sql SQL to send to database - * - * @return return FALSE, or the prepared statement, or the original sql if - * if the database does not support prepare. + * @param string $sql SQL to send to database * + * @return mixed|false The prepared statement, or the original sql if the + * database does not support prepare. */ function Prepare($sql) { return $sql; } /** + * Prepare a Stored Procedure and return the statement resource. + * * Some databases, eg. mssql require a different function for preparing * stored procedures. So we cannot use Prepare(). * - * Should prepare the stored procedure and return the stmt resource. - * For databases that do not support this, we return the $sql. To ensure - * compatibility with databases that do not support prepare: - * - * @param sql SQL to send to database + * For databases that do not support this, we return the $sql. * - * @return return FALSE, or the prepared statement, or the original sql if - * if the database does not support prepare. + * @param string $sql SQL to send to database + * @param bool $param * + * @return mixed|false The prepared statement, or the original sql if the + * database does not support prepare. */ function PrepareSP($sql,$param=true) { return $this->Prepare($sql,$param); @@ -860,6 +890,8 @@ if (!defined('_ADODB_LAYER')) { /** * Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de> + * + * @return string */ function QMagic($s) { return $this->qstr($s,get_magic_quotes_gpc()); @@ -891,17 +923,26 @@ if (!defined('_ADODB_LAYER')) { * Lock a row, will escalate and lock the table if row locking not supported * will normally free the lock at the end of the transaction * - * @param $table name of table to lock - * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock + * @param string $table name of table to lock + * @param string $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock + * @param string $col */ function RowLock($table,$where,$col='1 as adodbignore') { return false; } + /** + * @param string $table + * @return true + */ function CommitLock($table) { return $this->CommitTrans(); } + /** + * @param string $table + * @return true + */ function RollbackLock($table) { return $this->RollbackTrans(); } @@ -912,8 +953,9 @@ if (!defined('_ADODB_LAYER')) { * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical * for easy porting :-) * - * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM - * @returns The previous fetch mode + * @param int $mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM + * + * @return int Previous fetch mode */ function SetFetchMode($mode) { $old = $this->fetchMode; @@ -929,6 +971,11 @@ if (!defined('_ADODB_LAYER')) { /** * PEAR DB Compat - do not use internally. + * + * @param string $sql + * @param array|bool $inputarr + * + * @return ADORecordSet|bool */ function Query($sql, $inputarr=false) { $rs = $this->Execute($sql, $inputarr); @@ -940,8 +987,8 @@ if (!defined('_ADODB_LAYER')) { /** - * PEAR DB Compat - do not use internally - */ + * PEAR DB Compat - do not use internally + */ function LimitQuery($sql, $offset, $count, $params=false) { $rs = $this->SelectLimit($sql, $count, $offset, $params); if (!$rs && defined('ADODB_PEAR')) { @@ -952,8 +999,8 @@ if (!defined('_ADODB_LAYER')) { /** - * PEAR DB Compat - do not use internally - */ + * PEAR DB Compat - do not use internally + */ function Disconnect() { return $this->Close(); } @@ -1115,11 +1162,14 @@ if (!defined('_ADODB_LAYER')) { /** * Execute SQL * - * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text) - * @param [inputarr] holds the input data to bind to. Null elements will be set to null. - * @return RecordSet or false + * @param string $sql SQL statement to execute, or possibly an array + * holding prepared statement ($sql[0] will hold sql text) + * @param array|bool $inputarr holds the input data to bind to. + * Null elements will be set to null. + * + * @return ADORecordSet|bool */ - function Execute($sql,$inputarr=false) { + public function Execute($sql, $inputarr = false) { if ($this->fnExecute) { $fn = $this->fnExecute; $ret = $fn($this,$sql,$inputarr); @@ -1250,13 +1300,13 @@ if (!defined('_ADODB_LAYER')) { if( is_string($sql) ) { // Strips keyword used to help generate SELECT COUNT(*) queries // from SQL if it exists. - $sql = ADODB_str_replace( '_ADODB_COUNT', '', $sql ); + $sql = str_replace( '_ADODB_COUNT', '', $sql ); } if ($this->debug) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr); } else { @@ -1287,8 +1337,17 @@ if (!defined('_ADODB_LAYER')) { return $rs; } + if ($this->dataProvider == 'pdo' && $this->databaseType != 'pdo') { + // PDO uses a slightly different naming convention for the + // recordset class if the database type is changed, so we must + // treat it specifically. The mysql driver leaves the + // databaseType as pdo + $rsclass = $this->rsPrefix . 'pdo_' . $this->databaseType; + } else { + $rsclass = $this->rsPrefix . $this->databaseType; + } + // return real recordset from select statement - $rsclass = $this->rsPrefix.$this->databaseType; $rs = new $rsclass($this->_queryID,$this->fetchMode); $rs->connection = $this; // Pablo suggestion $rs->Init(); @@ -1325,12 +1384,14 @@ if (!defined('_ADODB_LAYER')) { } /** - * Generates a sequence id and stores it in $this->genID; + * Generates a sequence id and stores it in $this->genID. + * * GenID is only available if $this->hasGenID = true; * - * @param seqname name of sequence to use - * @param startID if sequence does not exist, start at this ID - * @return 0 if not supported, otherwise a sequence id + * @param string $seqname Name of sequence to use + * @param int $startID If sequence does not exist, start at this ID + * + * @return int Sequence id, 0 if not supported */ function GenID($seqname='adodbseq',$startID=1) { if (!$this->hasGenID) { @@ -1365,9 +1426,15 @@ if (!defined('_ADODB_LAYER')) { } /** - * @param $table string name of the table, not needed by all databases (eg. mysql), default '' - * @param $column string name of the column, not needed by all databases (eg. mysql), default '' - * @return the last inserted ID. Not all databases support this. + * Returns the last inserted ID. + * + * Not all databases support this feature. Some do not require to specify + * table or column name (e.g. MySQL). + * + * @param string $table Table name, default '' + * @param string $column Column name, default '' + * + * @return int The last inserted ID. */ function Insert_ID($table='',$column='') { if ($this->_logsql && $this->lastInsID) { @@ -1387,8 +1454,12 @@ if (!defined('_ADODB_LAYER')) { /** * Portable Insert ID. Pablo Roca <pabloroca#mvps.org> * - * @return the last inserted ID. All databases support this. But aware possible - * problems in multiuser environments. Heavy test this before deploying. + * @param string $table + * @param string $id + + * @return mixed The last inserted ID. All databases support this, but be + * aware of possible problems in multiuser environments. + * Heavily test this before deploying. */ function PO_Insert_ID($table="", $id="") { if ($this->hasInsertID){ @@ -1420,7 +1491,7 @@ if (!defined('_ADODB_LAYER')) { /** - * @return the last error message + * @return string the last error message */ function ErrorMsg() { if ($this->_errorMsg) { @@ -1432,7 +1503,7 @@ if (!defined('_ADODB_LAYER')) { /** - * @return the last error number. Normally 0 means no error. + * @return int the last error number. Normally 0 means no error. */ function ErrorNo() { return ($this->_errorMsg) ? -1 : 0; @@ -1483,13 +1554,15 @@ if (!defined('_ADODB_LAYER')) { /** * Choose a database to connect to. Many databases do not support this. * - * @param dbName is the name of the database to select - * @return true or false + * @param string $dbName the name of the database to select + * @return bool */ function SelectDB($dbName) {return false;} /** + * Select a limited number of rows. + * * Will select, getting rows from $offset (1-based), for $nrows. * This simulates the MySQL "select * from table limit $offset,$nrows" , and * the PostgreSQL "select * from table limit $nrows offset $offset". Note that @@ -1501,12 +1574,13 @@ if (!defined('_ADODB_LAYER')) { * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set) * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set * - * @param sql - * @param [offset] is the row to start calculations from (1-based) - * @param [nrows] is the number of rows to get - * @param [inputarr] array of bind variables - * @param [secs2cache] is a private parameter only used by jlim - * @return the recordset ($rs->databaseType == 'array') + * @param string $sql + * @param int $offset Row to start calculations from (1-based) + * @param int $nrows Number of rows to get + * @param array|bool $inputarr Array of bind variables + * @param int $secs2cache Private parameter only used by jlim + * + * @return ADORecordSet The recordset ($rs->databaseType == 'array') */ function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { $nrows = (int)$nrows; @@ -1523,32 +1597,47 @@ if (!defined('_ADODB_LAYER')) { } if ($offset <= 0) { - // access includes ties in result - if ($isaccess) { - $sql = preg_replace( - '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); + // access includes ties in result + if ($isaccess) { + $sql = preg_replace( + '/(^\s*select\s+(distinctrow|distinct)?)/i', + '\\1 '.$this->hasTop.' '.$nrows.' ', + $sql + ); - if ($secs2cache != 0) { - $ret = $this->CacheExecute($secs2cache, $sql,$inputarr); - } else { - $ret = $this->Execute($sql,$inputarr); - } - return $ret; // PHP5 fix - } else if ($ismssql){ - $sql = preg_replace( - '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); + if ($secs2cache != 0) { + $ret = $this->CacheExecute($secs2cache, $sql,$inputarr); } else { - $sql = preg_replace( - '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); + $ret = $this->Execute($sql,$inputarr); } + return $ret; // PHP5 fix + } else if ($ismssql){ + $sql = preg_replace( + '/(^\s*select\s+(distinctrow|distinct)?)/i', + '\\1 '.$this->hasTop.' '.$nrows.' ', + $sql + ); + } else { + $sql = preg_replace( + '/(^\s*select\s)/i', + '\\1 '.$this->hasTop.' '.$nrows.' ', + $sql + ); + } } else { $nn = $nrows + $offset; if ($isaccess || $ismssql) { $sql = preg_replace( - '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); + '/(^\s*select\s+(distinctrow|distinct)?)/i', + '\\1 '.$this->hasTop.' '.$nn.' ', + $sql + ); } else { $sql = preg_replace( - '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); + '/(^\s*select\s)/i', + '\\1 '.$this->hasTop.' '.$nn.' ', + $sql + ); } } } @@ -1578,7 +1667,9 @@ if (!defined('_ADODB_LAYER')) { /** * Create serializable recordset. Breaks rs link to connection. * - * @param rs the recordset to serialize + * @param ADORecordSet $rs the recordset to serialize + * + * @return ADORecordSet_array|bool the new recordset */ function SerializableRS(&$rs) { $rs2 = $this->_rs2rs($rs); @@ -1589,14 +1680,16 @@ if (!defined('_ADODB_LAYER')) { } /** - * Convert database recordset to an array recordset - * input recordset's cursor should be at beginning, and - * old $rs will be closed. + * Convert a database recordset to an array recordset. + * + * Input recordset's cursor should be at beginning, and old $rs will be closed. * - * @param rs the recordset to copy - * @param [nrows] number of rows to retrieve (optional) - * @param [offset] offset by number of rows (optional) - * @return the new recordset + * @param ADORecordSet $rs the recordset to copy + * @param int $nrows number of rows to retrieve (optional) + * @param int $offset offset by number of rows (optional) + * @param bool $close + * + * @return ADORecordSet_array|ADORecordSet|bool the new recordset */ function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) { if (! $rs) { @@ -1635,23 +1728,58 @@ if (!defined('_ADODB_LAYER')) { } /* - * Return all rows. Compat with PEAR DB + * Return all rows. + * + * Compat with PEAR DB. + * + * @param string $sql SQL statement + * @param array|bool $inputarr Input bind array + * + * @return array|false */ function GetAll($sql, $inputarr=false) { - $arr = $this->GetArray($sql,$inputarr); - return $arr; + return $this->GetArray($sql,$inputarr); } - function GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false) { + /** + * Execute statement and return rows in an array. + * + * The function executes a statement and returns all of the returned rows in + * an array, or false if the statement execution fails or if only 1 column + * is requested in the SQL statement. + * If no records match the provided SQL statement, an empty array is returned. + * + * @param string $sql SQL statement + * @param array|bool $inputarr input bind array + * @param bool $force_array + * @param bool $first2cols + * + * @return array|bool + */ + public function GetAssoc($sql, $inputarr = false, $force_array = false, $first2cols = false) { $rs = $this->Execute($sql, $inputarr); + if (!$rs) { + /* + * Execution failure + */ return false; } - $arr = $rs->GetAssoc($force_array,$first2cols); - return $arr; + return $rs->GetAssoc($force_array, $first2cols); } - function CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false) { + /** + * Search for the results of an executed query in the cache. + * + * @param int $secs2cache + * @param string|bool $sql SQL statement + * @param array|bool $inputarr input bind array + * @param bool $force_array + * @param bool $first2cols + * + * @return false|array + */ + public function CacheGetAssoc($secs2cache, $sql = false, $inputarr = false,$force_array = false, $first2cols = false) { if (!is_numeric($secs2cache)) { $first2cols = $force_array; $force_array = $inputarr; @@ -1660,18 +1788,18 @@ if (!defined('_ADODB_LAYER')) { if (!$rs) { return false; } - $arr = $rs->GetAssoc($force_array,$first2cols); - return $arr; + return $rs->GetAssoc($force_array, $first2cols); } /** - * Return first element of first row of sql statement. Recordset is disposed - * for you. - * - * @param sql SQL statement - * @param [inputarr] input bind array - */ - function GetOne($sql,$inputarr=false) { + * Return first element of first row of sql statement. Recordset is disposed + * for you. + * + * @param string $sql SQL statement + * @param array|bool $inputarr input bind array + * @return mixed + */ + public function GetOne($sql, $inputarr=false) { global $ADODB_COUNTRECS,$ADODB_GETONE_EOF; $crecs = $ADODB_COUNTRECS; @@ -1725,6 +1853,17 @@ if (!defined('_ADODB_LAYER')) { return $ret; } + /** + * Executes a statement and returns each row's first column in an array. + * + * @param string $sql SQL statement + * @param array|bool $inputarr input bind array + * @param bool $trim enables space trimming of the returned value. + * This is only relevant if the returned string + * is coming from a CHAR type field. + * + * @return array|bool 1D array containning the first row of the query + */ function GetCol($sql, $inputarr = false, $trim = false) { $rs = $this->Execute($sql, $inputarr); @@ -1796,10 +1935,13 @@ if (!defined('_ADODB_LAYER')) { /** - * - * @param sql SQL statement - * @param [inputarr] input bind array - */ + * Executes a statement and returns a the entire recordset in an array. + * + * @param string $sql SQL statement + * @param array|bool $inputarr input bind array + * + * @return array|false + */ function GetArray($sql,$inputarr=false) { global $ADODB_COUNTRECS; @@ -1809,8 +1951,7 @@ if (!defined('_ADODB_LAYER')) { $ADODB_COUNTRECS = $savec; if (!$rs) if (defined('ADODB_PEAR')) { - $cls = ADODB_PEAR_Error(); - return $cls; + return ADODB_PEAR_Error(); } else { return false; } @@ -1820,8 +1961,7 @@ if (!defined('_ADODB_LAYER')) { } function CacheGetAll($secs2cache,$sql=false,$inputarr=false) { - $arr = $this->CacheGetArray($secs2cache,$sql,$inputarr); - return $arr; + return $this->CacheGetArray($secs2cache,$sql,$inputarr); } function CacheGetArray($secs2cache,$sql=false,$inputarr=false) { @@ -1834,8 +1974,7 @@ if (!defined('_ADODB_LAYER')) { if (!$rs) if (defined('ADODB_PEAR')) { - $cls = ADODB_PEAR_Error(); - return $cls; + return ADODB_PEAR_Error(); } else { return false; } @@ -1854,8 +1993,10 @@ if (!defined('_ADODB_LAYER')) { * Return one row of sql statement. Recordset is disposed for you. * Note that SelectLimit should not be called. * - * @param sql SQL statement - * @param [inputarr] input bind array + * @param string $sql SQL statement + * @param array|bool $inputarr input bind array + * + * @return array|false Array containing the first row of the query */ function GetRow($sql,$inputarr=false) { global $ADODB_COUNTRECS; @@ -1879,6 +2020,12 @@ if (!defined('_ADODB_LAYER')) { return false; } + /** + * @param int $secs2cache + * @param string|false $sql + * @param mixed[]|bool $inputarr + * @return mixed[]|bool + */ function CacheGetRow($secs2cache,$sql=false,$inputarr=false) { $rs = $this->CacheExecute($secs2cache,$sql,$inputarr); if ($rs) { @@ -1905,7 +2052,7 @@ if (!defined('_ADODB_LAYER')) { * $table table name * $fieldArray associative array of data (you must quote strings yourself). * $keyCol the primary key field name or if compound key, array of field names - * autoQuote set to true to use a hueristic to quote strings. Works with nulls and numbers + * autoQuote set to true to use a heuristic to quote strings. Works with nulls and numbers * but does not work with dates nor SQL functions. * has_autoinc the primary key is an auto-inc field, so skip in insert. * @@ -1917,7 +2064,7 @@ if (!defined('_ADODB_LAYER')) { function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc); @@ -1935,12 +2082,13 @@ if (!defined('_ADODB_LAYER')) { * * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set * - * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional - * @param sql - * @param [offset] is the row to start calculations from (1-based) - * @param [nrows] is the number of rows to get - * @param [inputarr] array of bind variables - * @return the recordset ($rs->databaseType == 'array') + * @param int $secs2cache Seconds to cache data, set to 0 to force query. This is optional + * @param string $sql + * @param int $offset Row to start calculations from (1-based) + * @param int $nrows Number of rows to get + * @param array $inputarr Array of bind variables + * + * @return ADORecordSet The recordset ($rs->databaseType == 'array') */ function CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false) { if (!is_numeric($secs2cache)) { @@ -2023,11 +2171,12 @@ if (!defined('_ADODB_LAYER')) { /** * Execute SQL, caching recordsets. * - * @param [secs2cache] seconds to cache data, set to 0 to force query. - * This is an optional parameter. - * @param sql SQL statement to execute - * @param [inputarr] holds the input data to bind to - * @return RecordSet or false + * @param int $secs2cache Seconds to cache data, set to 0 to force query. + * This is an optional parameter. + * @param string|bool $sql SQL statement to execute + * @param array|bool $inputarr Holds the input data to bind + * + * @return ADORecordSet RecordSet or false */ function CacheExecute($secs2cache,$sql=false,$inputarr=false) { global $ADODB_CACHE; @@ -2144,6 +2293,10 @@ if (!defined('_ADODB_LAYER')) { $forceUpdate means that even if the data has not changed, perform update. */ function AutoExecute($table, $fields_values, $mode = 'INSERT', $where = false, $forceUpdate = true, $magicq = false) { + if (empty($fields_values)) { + $this->outp_throw('AutoExecute: Empty fields array', 'AutoExecute'); + return false; + } if ($where === false && ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) ) { $this->outp_throw('AutoExecute: Illegal mode=UPDATE with empty WHERE clause', 'AutoExecute'); return false; @@ -2202,7 +2355,7 @@ if (!defined('_ADODB_LAYER')) { // ******************************************************** if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force); } @@ -2222,7 +2375,7 @@ if (!defined('_ADODB_LAYER')) { $force = $ADODB_FORCE_TYPE; } if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force); } @@ -2415,7 +2568,7 @@ if (!defined('_ADODB_LAYER')) { /** * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans(). * - * @return true if succeeded or false if database does not support transactions + * @return bool true if succeeded or false if database does not support transactions */ function BeginTrans() { if ($this->debug) { @@ -2475,9 +2628,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } /** - * If database does not support transactions, always return true as data always commited + * If database does not support transactions, always return true as data always committed * - * @param $ok set to false to rollback transaction, true to commit + * @param bool $ok set to false to rollback transaction, true to commit * * @return true/false. */ @@ -2489,7 +2642,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * If database does not support transactions, rollbacks always fail, so return false * - * @return true/false. + * @return bool */ function RollbackTrans() { return false; @@ -2500,7 +2653,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * return the databases that the driver can connect to. * Some databases will return an empty array. * - * @return an array of database names. + * @return array|bool an array of database names. */ function MetaDatabases() { global $ADODB_FETCH_MODE; @@ -2724,14 +2877,16 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } /** + * Concatenate strings. + * * Different SQL databases used different methods to combine strings together. * This function provides a wrapper. * - * param s variable number of string parameters - * * Usage: $db->Concat($str1,$str2); * - * @return concatenated string + * @param string $s Variable number of string parameters + * + * @return string concatenated string */ function Concat() { $arr = func_get_args(); @@ -2742,9 +2897,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Converts a date "d" to a string that the database can understand. * - * @param d a date in Unix date time format. + * @param mixed $d a date in Unix date time format. * - * @return date string in database date format + * @return string date string in database date format */ function DBDate($d, $isfld=false) { if (empty($d) && $d !== 0) { @@ -2796,9 +2951,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Converts a timestamp "ts" to a string that the database can understand. * - * @param ts a timestamp in Unix date time format. + * @param int|object $ts A timestamp in Unix date time format. * - * @return timestamp string in database timestamp format + * @return string $timestamp string in database timestamp format */ function DBTimeStamp($ts,$isfld=false) { if (empty($ts) && $ts !== 0) { @@ -2829,9 +2984,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Also in ADORecordSet. - * @param $v is a date string in YYYY-MM-DD format + * @param mixed $v is a date string in YYYY-MM-DD format * - * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + * @return int|false Date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ static function UnixDate($v) { if (is_object($v)) { @@ -2858,9 +3013,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Also in ADORecordSet. - * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format + * @param string|object $v is a timestamp string in YYYY-MM-DD HH-NN-SS format * - * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + * @return int|false Date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ static function UnixTimeStamp($v) { if (is_object($v)) { @@ -2885,14 +3040,15 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } /** - * Also in ADORecordSet. - * * Format database date based on user defined format. * - * @param v is the character date in YYYY-MM-DD format, returned by database - * @param fmt is the format to apply to it, using date() + * Also in ADORecordSet. + * + * @param mixed $v Date in YYYY-MM-DD format, returned by database + * @param string $fmt Format to apply, using date() + * @param bool $gmt * - * @return a date formated as user desires + * @return string Formatted date */ function UserDate($v,$fmt='Y-m-d',$gmt=false) { $tt = $this->UnixDate($v); @@ -2911,11 +3067,13 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } /** + * Format timestamp based on user defined format. * - * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format - * @param fmt is the format to apply to it, using date() + * @param mixed $v Date in YYYY-MM-DD hh:mm:ss format + * @param string $fmt Format to apply, using date() + * @param bool $gmt * - * @return a timestamp formated as user desires + * @return string Formatted timestamp */ function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false) { if (!isset($v)) { @@ -2936,13 +3094,22 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); } + /** + * @param string $s + * @param bool [$magic_quotes] + * @return mixed + */ function escape($s,$magic_quotes=false) { return $this->addq($s,$magic_quotes); } /** - * Quotes a string, without prefixing nor appending quotes. - */ + * Quotes a string, without prefixing nor appending quotes. + * + * @param string $s + * @param bool [$magic_quotes] + * @return mixed + */ function addq($s,$magic_quotes=false) { if (!$magic_quotes) { if ($this->replaceQuote[0] == '\\') { @@ -2971,11 +3138,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * to the string single-quotes. * An example is $db->qstr("Don't bother",magic_quotes_runtime()); * - * @param s the string to quote - * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). - * This undoes the stupidity of magic quotes for GPC. + * @param string $s The string to quote + * @param bool $magic_quotes If $s is GET/POST var, set to get_magic_quotes_gpc(). + * This undoes the stupidity of magic quotes for GPC. * - * @return quoted string to be sent back to database + * @return string Quoted string to be sent back to database */ function qstr($s,$magic_quotes=false) { if (!$magic_quotes) { @@ -3002,26 +3169,25 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** - * Will select the supplied $page number from a recordset, given that it is paginated in pages of - * $nrows rows per page. It also saves two boolean values saying if the given page is the first - * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination. - * - * See docs-adodb.htm#ex8 for an example of usage. - * - * @param sql - * @param nrows is the number of rows per page to get - * @param page is the page number to get (1-based) - * @param [inputarr] array of bind variables - * @param [secs2cache] is a private parameter only used by jlim - * @return the recordset ($rs->databaseType == 'array') - * - * NOTE: phpLens uses a different algorithm and does not use PageExecute(). - * - */ + * Will select the supplied $page number from a recordset, given that it is paginated in pages of + * $nrows rows per page. It also saves two boolean values saying if the given page is the first + * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination. + * + * See docs-adodb.htm#ex8 for an example of usage. + * NOTE: phpLens uses a different algorithm and does not use PageExecute(). + * + * @param string $sql + * @param int $nrows Number of rows per page to get + * @param int $page Page number to get (1-based) + * @param mixed[]|bool $inputarr Array of bind variables + * @param int $secs2cache Private parameter only used by jlim + * + * @return mixed the recordset ($rs->databaseType == 'array') + */ function PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } if ($this->pageExecuteCountRows) { $rs = _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache); @@ -3037,12 +3203,12 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * $nrows rows per page. It also saves two boolean values saying if the given page is the first * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination. * - * @param secs2cache seconds to cache data, set to 0 to force query - * @param sql - * @param nrows is the number of rows per page to get - * @param page is the page number to get (1-based) - * @param [inputarr] array of bind variables - * @return the recordset ($rs->databaseType == 'array') + * @param int $secs2cache seconds to cache data, set to 0 to force query + * @param string $sql + * @param int $nrows is the number of rows per page to get + * @param int $page is the page number to get (1-based) + * @param mixed[]|bool $inputarr array of bind variables + * @return mixed the recordset ($rs->databaseType == 'array') */ function CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) { /*switch($this->dataProvider) { @@ -3051,8 +3217,79 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 break; default: $secs2cache = 0; break; }*/ - $rs = $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache); - return $rs; + return $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache); + } + + /** + * Returns the maximum size of a MetaType C field. If the method + * is not defined in the driver returns ADODB_STRINGMAX_NOTSET + * + * @return int + */ + function charMax() { + return ADODB_STRINGMAX_NOTSET; + } + + /** + * Returns the maximum size of a MetaType X field. If the method + * is not defined in the driver returns ADODB_STRINGMAX_NOTSET + * + * @return int + */ + function textMax() { + return ADODB_STRINGMAX_NOTSET; + } + + /** + * Returns a substring of a varchar type field + * + * Some databases have variations of the parameters, which is why + * we have an ADOdb function for it + * + * @param string $fld The field to sub-string + * @param int $start The start point + * @param int $length An optional length + * + * @return string The SQL text + */ + function substr($fld,$start,$length=0) { + $text = "{$this->substr}($fld,$start"; + if ($length > 0) + $text .= ",$length"; + $text .= ')'; + return $text; + } + + /* + * Formats the date into Month only format MM with leading zeroes + * + * @param string $fld The name of the date to format + * + * @return string The SQL text + */ + function month($fld) { + return $this->sqlDate('m',$fld); + } + + /* + * Formats the date into Day only format DD with leading zeroes + * + * @param string $fld The name of the date to format + * @return string The SQL text + */ + function day($fld) { + return $this->sqlDate('d',$fld); + } + + /* + * Formats the date into year only format YYYY + * + * @param string $fld The name of the date to format + * + * @return string The SQL text + */ + function year($fld) { + return $this->sqlDate('Y',$fld); } /** @@ -3061,7 +3298,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * By clearing the message, it becomes possible to detect whether a new error * has occurred, even when it is the same error as before being repeated. * - * @return array|null Array if an error has previously occurred. Null otherwise. + * @return mixed[]|null Array if an error has previously occurred. Null otherwise. */ protected function resetLastError() { $error = error_get_last(); @@ -3075,18 +3312,18 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Compare a previously stored error message with the last error recorded by PHP - * to determine whether a new error has occured. + * to determine whether a new error has occurred. * - * @param array|null $old Optional. Previously stored return value of error_get_last(). + * @param mixed[]|null $old Optional. Previously stored return value of error_get_last(). * - * @return string The error message if a new error has occured - * or an empty string if no (new) errors have occured.. + * @return string The error message if a new error has occurred + * or an empty string if no (new) errors have occurred.. */ protected function getChangedErrorMsg($old = null) { $new = error_get_last(); if (is_null($new)) { - // No error has occured yet at all. + // No error has occurred yet at all. return ''; } @@ -3172,6 +3409,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 var $databaseType = false; var $EOF = true; var $_numOfRows = 0; + /** @var bool|array */ var $fields = false; var $connection = false; @@ -3246,7 +3484,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 // DATE AND TIME FUNCTIONS //============================================================================================== if (!defined('ADODB_DATE_VERSION')) { - include(ADODB_DIR.'/adodb-time.inc.php'); + include_once(ADODB_DIR.'/adodb-time.inc.php'); } //============================================================================================== @@ -3304,6 +3542,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * public variables */ var $dataProvider = "native"; + /** @var bool|array */ var $fields = false; /// holds the current row data var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob /// in other words, we use a text area for editing. @@ -3342,7 +3581,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Constructor * - * @param queryID this is the queryID returned by ADOConnection->_query() + * @param resource|int queryID this is the queryID returned by ADOConnection->_query() * */ function __construct($queryID) { @@ -3410,7 +3649,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } return _adodb_getmenu($this, $name, $defstr, $blank1stItem, $multiple, $size, $selectAttr, $compareFirstCol); @@ -3442,7 +3681,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } return _adodb_getmenu_gp($this, $name, $defstr, $blank1stItem, $multiple, $size, $selectAttr, $compareFirstCol); @@ -3503,15 +3742,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * return recordset as a 2-dimensional array. * - * @param [nRows] is the number of rows to return. -1 means every row. + * @param int $nRows Number of rows to return. -1 means every row. * - * @return an array indexed by the rows (0-based) from the recordset + * @return array indexed by the rows (0-based) from the recordset */ function GetArray($nRows = -1) { - global $ADODB_EXTENSION; if ($ADODB_EXTENSION) { - $results = adodb_getall($this,$nRows); - return $results; - } $results = array(); $cnt = 0; while (!$this->EOF && $nRows != $cnt) { @@ -3523,8 +3758,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } function GetAll($nRows = -1) { - $arr = $this->GetArray($nRows); - return $arr; + return $this->GetArray($nRows); } /* @@ -3542,12 +3776,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * @param offset is the row to start calculations from (1-based) * @param [nrows] is the number of rows to return * - * @return an array indexed by the rows (0-based) from the recordset + * @return array an array indexed by the rows (0-based) from the recordset */ function GetArrayLimit($nrows,$offset=-1) { if ($offset <= 0) { - $arr = $this->GetArray($nrows); - return $arr; + return $this->GetArray($nrows); } $this->Move($offset); @@ -3568,137 +3801,163 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * * @param [nRows] is the number of rows to return. -1 means every row. * - * @return an array indexed by the rows (0-based) from the recordset + * @return array an array indexed by the rows (0-based) from the recordset */ function GetRows($nRows = -1) { - $arr = $this->GetArray($nRows); - return $arr; + return $this->GetArray($nRows); } /** - * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. - * The first column is treated as the key and is not included in the array. - * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless - * $force_array == true. + * return whole recordset as a 2-dimensional associative array if + * there are more than 2 columns. The first column is treated as the + * key and is not included in the array. If there is only 2 columns, + * it will return a 1 dimensional array of key-value pairs unless + * $force_array == true. This recordset method is currently part of + * the API, but may not be in later versions of ADOdb. By preference, use + * ADOconnnection::getAssoc() * - * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional - * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing, - * read the source. + * @param bool $force_array (optional) Has only meaning if we have 2 data + * columns. If false, a 1 dimensional + * array is returned, otherwise a 2 dimensional + * array is returned. If this sounds confusing, + * read the source. * - * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and - * instead of returning array[col0] => array(remaining cols), return array[col0] => col1 + * @param bool $first2cols (optional) Means if there are more than + * 2 cols, ignore the remaining cols and + * instead of returning + * array[col0] => array(remaining cols), + * return array[col0] => col1 + * + * @return mixed[]|false * - * @return an associative array indexed by the first column of the array, - * or false if the data has less than 2 cols. */ - function GetAssoc($force_array = false, $first2cols = false) { - global $ADODB_EXTENSION; - - $cols = $this->_numOfFields; - if ($cols < 2) { - return false; - } + function getAssoc($force_array = false, $first2cols = false) + { + /* + * Insufficient rows to show data + */ + if ($this->_numOfFields < 2) + return; - // Empty recordset + /* + * Empty recordset + */ if (!$this->fields) { return array(); } - // Determine whether the array is associative or 0-based numeric - $numIndex = array_keys($this->fields) == range(0, count($this->fields) - 1); + /* + * The number of fields is half the actual returned in BOTH mode + */ + $numberOfFields = $this->_numOfFields; - $results = array(); + /* + * Get the fetch mode when the call was executed, this may be + * different than ADODB_FETCH_MODE + */ + $fetchMode = $this->connection->fetchMode; + if ($fetchMode == ADODB_FETCH_BOTH) { + /* + * If we are using BOTH, we present the data as if it + * was in ASSOC mode. This could be enhanced by adding + * a BOTH_ASSOC_MODE class property + * We build a template of numeric keys. you could improve the + * speed by caching this, indexed by number of keys + */ + $testKeys = array_fill(0,$numberOfFields,0); + } - if (!$first2cols && ($cols > 2 || $force_array)) { - if ($ADODB_EXTENSION) { - if ($numIndex) { - while (!$this->EOF) { - $results[trim($this->fields[0])] = array_slice($this->fields, 1); - adodb_movenext($this); - } - } else { - while (!$this->EOF) { - // Fix for array_slice re-numbering numeric associative keys - $keys = array_slice(array_keys($this->fields), 1); - $sliced_array = array(); + $showArrayMethod = 0; - foreach($keys as $key) { - $sliced_array[$key] = $this->fields[$key]; - } + if ($numberOfFields == 2) + /* + * Key is always value of first element + * Value is always value of second element + */ + $showArrayMethod = 1; - $results[trim(reset($this->fields))] = $sliced_array; - adodb_movenext($this); - } - } - } else { - if ($numIndex) { - while (!$this->EOF) { - $results[trim($this->fields[0])] = array_slice($this->fields, 1); - $this->MoveNext(); - } - } else { - while (!$this->EOF) { - // Fix for array_slice re-numbering numeric associative keys - $keys = array_slice(array_keys($this->fields), 1); - $sliced_array = array(); + if ($force_array) + $showArrayMethod = 0; - foreach($keys as $key) { - $sliced_array[$key] = $this->fields[$key]; - } + if ($first2cols) + $showArrayMethod = 1; - $results[trim(reset($this->fields))] = $sliced_array; - $this->MoveNext(); - } - } + $results = array(); + + while (!$this->EOF){ + + $myFields = $this->fields; + + if ($fetchMode == ADODB_FETCH_BOTH) { + /* + * extract the associative keys + */ + $myFields = array_diff_key($myFields,$testKeys); } - } else { - if ($ADODB_EXTENSION) { - // return scalar values - if ($numIndex) { - while (!$this->EOF) { - // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string - $results[trim(($this->fields[0]))] = $this->fields[1]; - adodb_movenext($this); - } - } else { - while (!$this->EOF) { - // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string - $v1 = trim(reset($this->fields)); - $v2 = ''.next($this->fields); - $results[$v1] = $v2; - adodb_movenext($this); - } - } - } else { - if ($numIndex) { - while (!$this->EOF) { - // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string - $results[trim(($this->fields[0]))] = $this->fields[1]; - $this->MoveNext(); - } - } else { - while (!$this->EOF) { - // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string - $v1 = trim(reset($this->fields)); - $v2 = ''.next($this->fields); - $results[$v1] = $v2; - $this->MoveNext(); - } + + /* + * key is value of first element, rest is data, + * The key is not case processed + */ + $key = array_shift($myFields); + + switch ($showArrayMethod) { + case 0: + + if ($fetchMode == ADODB_FETCH_ASSOC + || $fetchMode == ADODB_FETCH_BOTH) + { + /* + * The driver should have already handled the key + * casing, but in case it did not. We will check and force + * this in later versions of ADOdb + */ + if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER) + $myFields = array_change_key_case($myFields,CASE_UPPER); + + elseif (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) + $myFields = array_change_key_case($myFields,CASE_LOWER); + + /* + * We have already shifted the key off + * the front, so the rest is the value + */ + $results[$key] = $myFields; + } + else + /* + * I want the values in a numeric array, + * nicely re-indexed from zero + */ + $results[$key] = array_values($myFields); + break; + + case 1: + + /* + * Don't care how long the array is, + * I just want value of second column, and it doesn't + * matter whether the array is associative or numeric + */ + $results[$key] = array_shift($myFields); + break; } - } - $ref = $results; # workaround accelerator incompat with PHP 4.4 :( - return $ref; + $this->MoveNext(); + } + /* + * Done + */ + return $results; } - /** * - * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format - * @param fmt is the format to apply to it, using date() + * @param mixed $v is the character timestamp in YYYY-MM-DD hh:mm:ss format + * @param string [$fmt] is the format to apply to it, using date() * - * @return a timestamp formated as user desires + * @return string a timestamp formated as user desires */ function UserTimeStamp($v,$fmt='Y-m-d H:i:s') { if (is_numeric($v) && strlen($v)<14) { @@ -3717,10 +3976,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** - * @param v is the character date in YYYY-MM-DD format, returned by database - * @param fmt is the format to apply to it, using date() + * @param mixed $v is the character date in YYYY-MM-DD format, returned by database + * @param string $fmt is the format to apply to it, using date() * - * @return a date formated as user desires + * @return string a date formatted as user desires */ function UserDate($v,$fmt='Y-m-d') { $tt = $this->UnixDate($v); @@ -3737,9 +3996,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** - * @param $v is a date string in YYYY-MM-DD format + * @param mixed $v is a date string in YYYY-MM-DD format * - * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + * @return string date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ static function UnixDate($v) { return ADOConnection::UnixDate($v); @@ -3747,9 +4006,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** - * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format + * @param string|object $v is a timestamp string in YYYY-MM-DD HH-NN-SS format * - * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + * @return mixed date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ static function UnixTimeStamp($v) { return ADOConnection::UnixTimeStamp($v); @@ -3765,26 +4024,30 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** - * PEAR DB compat, number of rows - */ + * PEAR DB compat, number of rows + * + * @return int + */ function NumRows() { return $this->_numOfRows; } /** - * PEAR DB compat, number of cols - */ + * PEAR DB compat, number of cols + * + * @return int + */ function NumCols() { return $this->_numOfFields; } /** - * Fetch a row, returning false if no more rows. - * This is PEAR DB compat mode. - * - * @return false or array containing the current record - */ + * Fetch a row, returning false if no more rows. + * This is PEAR DB compat mode. + * + * @return mixed[]|false false or array containing the current record + */ function FetchRow() { if ($this->EOF) { return false; @@ -3802,7 +4065,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * Fetch a row, returning PEAR_Error if no more rows. * This is PEAR DB compat mode. * - * @return DB_OK or error object + * @param mixed[]|false $arr + * + * @return mixed DB_OK or error object */ function FetchInto(&$arr) { if ($this->EOF) { @@ -3817,7 +4082,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Move to the first row in the recordset. Many databases do NOT support this. * - * @return true or false + * @return bool true or false */ function MoveFirst() { if ($this->_currentRow == 0) { @@ -3830,7 +4095,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Move to the last row in the recordset. * - * @return true or false + * @return bool true or false */ function MoveLast() { if ($this->_numOfRows >= 0) { @@ -3852,7 +4117,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Move to next record in the recordset. * - * @return true if there still rows available, or false if there are no more rows (EOF). + * @return bool true if there still rows available, or false if there are no more rows (EOF). */ function MoveNext() { if (!$this->EOF) { @@ -3877,9 +4142,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * Random access to a specific row in the recordset. Some databases do not support * access to previous rows in the databases (no scrolling backwards). * - * @param rowNumber is the row to move to (0-based) + * @param int $rowNumber is the row to move to (0-based) * - * @return true if there still rows available, or false if there are no more rows (EOF). + * @return bool true if there still rows available, or false if there are no more rows (EOF). */ function Move($rowNumber = 0) { $this->EOF = false; @@ -3911,18 +4176,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 if ($rowNumber < $this->_currentRow) { return false; } - global $ADODB_EXTENSION; - if ($ADODB_EXTENSION) { - while (!$this->EOF && $this->_currentRow < $rowNumber) { - adodb_movenext($this); - } - } else { - while (! $this->EOF && $this->_currentRow < $rowNumber) { - $this->_currentRow++; + while (! $this->EOF && $this->_currentRow < $rowNumber) { + $this->_currentRow++; - if (!$this->_fetch()) { - $this->EOF = true; - } + if (!$this->_fetch()) { + $this->EOF = true; } } return !($this->EOF); @@ -3938,9 +4196,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * Get the value of a field in the current row by column name. * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM. * - * @param colname is the field to access + * @param string $colname is the field to access * - * @return the value of $colname column + * @return mixed the value of $colname column */ function Fields($colname) { return $this->fields[$colname]; @@ -3949,6 +4207,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Defines the function to use for table fields case conversion * depending on ADODB_ASSOC_CASE + * + * @param int [$case] + * * @return string strtolower/strtoupper or false if no conversion needed */ protected function AssocCaseConvertFunction($case = ADODB_ASSOC_CASE) { @@ -3966,7 +4227,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Builds the bind array associating keys to recordset fields * - * @param int $upper Case for the array keys, defaults to uppercase + * @param int [$upper] Case for the array keys, defaults to uppercase * (see ADODB_ASSOC_CASE_xxx constants) */ function GetAssocKeys($upper = ADODB_ASSOC_CASE) { @@ -4003,7 +4264,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * Use associative array to get fields array for databases that do not support * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it * - * @param int $upper Case for the array keys, defaults to uppercase + * @param int [$upper] Case for the array keys, defaults to uppercase * (see ADODB_ASSOC_CASE_xxx constants) */ function GetRowAssoc($upper = ADODB_ASSOC_CASE) { @@ -4026,7 +4287,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Clean up recordset * - * @return true or false + * @return bool */ function Close() { // free connection object - this seems to globally free the object @@ -4040,19 +4301,21 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } /** - * synonyms RecordCount and RowCount + * Synonyms RecordCount and RowCount * - * @return the number of rows or -1 if this is not supported + * @return int Number of rows or -1 if this is not supported */ function RecordCount() { return $this->_numOfRows; } - /* - * If we are using PageExecute(), this will return the maximum possible rows - * that can be returned when paging a recordset. - */ + /** + * If we are using PageExecute(), this will return the maximum possible rows + * that can be returned when paging a recordset. + * + * @return int + */ function MaxRecordCount() { return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount(); } @@ -4118,7 +4381,6 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 return $this->_numOfFields; } - /** * Get the ADOFieldObject of a specific column. * @@ -4137,9 +4399,12 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * */ function FieldTypesArray() { - $arr = array(); - for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) - $arr[] = $this->FetchField($i); + static $arr = array(); + if (empty($arr)) { + for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) { + $arr[] = $this->FetchField($i); + } + } return $arr; } @@ -4150,8 +4415,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * @return the object with the properties set to the fields of the current row */ function FetchObj() { - $o = $this->FetchObject(false); - return $o; + return $this->FetchObject(false); } /** @@ -4172,11 +4436,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } } $i = 0; - if (PHP_VERSION >= 5) { - $o = clone($this->_obj); - } else { - $o = $this->_obj; - } + $o = clone($this->_obj); for ($i=0; $i <$this->_numOfFields; $i++) { $name = $this->_names[$i]; @@ -4201,8 +4461,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * Fixed bug reported by tim@orotech.net */ function FetchNextObj() { - $o = $this->FetchNextObject(false); - return $o; + return $this->FetchNextObject(false); } @@ -4261,6 +4520,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 $len = $fieldobj->max_length; } + // changed in 2.32 to hashing instead of switch stmt for speed... static $typeMap = array( 'VARCHAR' => 'C', @@ -4367,9 +4627,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 "SQLBOOL" => 'L' ); + $tmap = false; $t = strtoupper($t); - $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; + $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : ADODB_DEFAULT_METATYPE; switch ($tmap) { case 'C': // is the char field is too long, return as text field... @@ -4522,7 +4783,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } $hdr = true; @@ -4556,10 +4817,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 * * @param array is a 2-dimensional array holding the data. * The first row should hold the column names - * unless paramter $colnames is used. + * unless parameter $colnames is used. * @param typearr holds an array of types. These are the same types * used in MetaTypes (C,B,L,I,N). - * @param [colnames] array of column names. If set, then the first row of + * @param string[]|false [$colnames] array of column names. If set, then the first row of * $array should not hold the column names. */ function InitArray($array,$typearr,$colnames=false) { @@ -4577,10 +4838,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 /** * Setup the Array and datatype file objects * - * @param array is a 2-dimensional array holding the data. + * @param array $array 2-dimensional array holding the data * The first row should hold the column names - * unless paramter $colnames is used. - * @param fieldarr holds an array of ADOFieldObject's. + * unless parameter $colnames is used. + * @param array $fieldarr Array of ADOFieldObject's. */ function InitArrayFields(&$array,&$fieldarr) { $this->_array = $array; @@ -4591,12 +4852,15 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 $this->Init(); } + /** + * @param int [$nRows] + * @return array + */ function GetArray($nRows=-1) { if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) { return $this->_array; } else { - $arr = ADORecordSet::GetArray($nRows); - return $arr; + return ADORecordSet::GetArray($nRows); } } @@ -4611,7 +4875,12 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 : sizeof($this->_types); } - /* Use associative array to get fields array */ + /** + * Use associative array to get fields array + * + * @param string $colname + * @return mixed + */ function Fields($colname) { $mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode; @@ -4631,6 +4900,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 return $this->fields[$this->bind[strtoupper($colname)]]; } + /** + * @param int [$fieldOffset] + * + * @return \ADOFieldObject + */ function FetchField($fieldOffset = -1) { if (isset($this->_fieldobjects)) { return $this->_fieldobjects[$fieldOffset]; @@ -4643,6 +4917,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 return $o; } + /** + * @param int $row + * @return bool + */ function _seek($row) { if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) { $this->_currentRow = $row; @@ -4655,6 +4933,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 return false; } + /** + * @return bool + */ function MoveNext() { if (!$this->EOF) { $this->_currentRow++; @@ -4678,6 +4959,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 return false; } + /** + * @return bool + */ function _fetch() { $pos = $this->_currentRow; @@ -4725,9 +5009,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 $db = strtolower($dbType); switch ($db) { case 'ado': - if (PHP_VERSION >= 5) { - $db = 'ado5'; - } + $db = 'ado5'; $class = 'ado'; break; @@ -4738,11 +5020,26 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 case 'pgsql': case 'postgres': - $class = $db = 'postgres8'; + $class = $db = 'postgres9'; + break; + + case 'mysql': + // mysql driver deprecated since 5.5, removed in 7.0 + // automatically switch to mysqli + if(version_compare(PHP_VERSION, '7.0.0', '>=')) { + $db = 'mysqli'; + } + $class = $db; break; default: - $class = $db; break; + if (substr($db, 0, 4) === 'pdo_') { + ADOConnection::outp("Invalid database type: $db"); + return false; + } + + $class = $db; + break; } $file = "drivers/adodb-$db.inc.php"; @@ -4762,20 +5059,24 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } /** - * synonym for ADONewConnection for people like me who cannot remember the correct name + * Synonym for ADONewConnection for people like me who cannot remember the correct name + * + * @param string [$db] + * + * @return ADOConnection|false */ function NewADOConnection($db='') { - $tmp = ADONewConnection($db); - return $tmp; + return ADONewConnection($db); } /** * Instantiate a new Connection class for a specific database driver. * - * @param [db] is the database Connection object to create. If undefined, + * @param string $db Database Connection object to create. If undefined, * use the last database driver that was loaded by ADOLoadCode(). * - * @return the freshly created instance of the Connection class. + * @return ADOConnection|false The freshly created instance of the Connection class + * or false in case of error. */ function ADONewConnection($db='') { global $ADODB_NEWCONNECTION, $ADODB_LASTDB; @@ -5030,9 +5331,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 if (!class_exists($class)) { return false; } - $perf = new $class($conn); - return $perf; + return new $class($conn); } function NewDataDictionary(&$conn,$drivername=false) { @@ -5094,7 +5394,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) { - include(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-lib.inc.php'); } return _adodb_backtrace($printOrArr,$levels,0,$ishtml); } diff --git a/composer.json b/composer.json index 30fcc358..ef5f5438 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ }, "require" : { - "php" : ">=5.3.2" + "php" : "^5.3.2 || ^7.0" }, "autoload" : { diff --git a/cute_icons_for_site/adodb.gif b/cute_icons_for_site/adodb.gif Binary files differdeleted file mode 100644 index c5e8dfc6..00000000 --- a/cute_icons_for_site/adodb.gif +++ /dev/null diff --git a/cute_icons_for_site/adodb2.gif b/cute_icons_for_site/adodb2.gif Binary files differdeleted file mode 100644 index f12ae203..00000000 --- a/cute_icons_for_site/adodb2.gif +++ /dev/null diff --git a/datadict/datadict-db2.inc.php b/datadict/datadict-db2.inc.php index 748976c2..397f40f7 100644 --- a/datadict/datadict-db2.inc.php +++ b/datadict/datadict-db2.inc.php @@ -1,5 +1,4 @@ <?php - /** @version v5.21.0-dev ??-???-2016 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved. @@ -18,7 +17,12 @@ class ADODB2_db2 extends ADODB_DataDict { var $databaseType = 'db2'; var $seqField = false; + var $dropCol = 'ALTER TABLE %s DROP COLUMN %s'; + + public $blobAllowsDefaultValue = true; + public $blobAllowsNotNull = true; + function ActualType($meta) { switch($meta) { @@ -60,21 +64,66 @@ class ADODB2_db2 extends ADODB_DataDict { return $suffix; } - function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + function alterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { - if ($this->debug) ADOConnection::outp("AlterColumnSQL not supported"); - return array(); + $tabname = $this->TableName ($tabname); + $sql = array(); + list($lines,$pkey,$idxs) = $this->_GenFields($flds); + // genfields can return FALSE at times + if ($lines == null) $lines = array(); + $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' '; + + $dataTypeWords = array('SET','DATA','TYPE'); + + foreach($lines as $v) + { + /* + * We must now post-process the line to insert the 'SET DATA TYPE' + * text into the alter statement + */ + $e = explode(' ',$v); + + array_splice($e,1,0,$dataTypeWords); + + $v = implode(' ',$e); + + $sql[] = $alter . $v; + } + if (is_array($idxs)) + { + foreach($idxs as $idx => $idxdef) { + $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); + $sql = array_merge($sql, $sql_idxs); + } + + } + return $sql; } - function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + + function dropColumnSql($tabname, $flds, $tableflds='',$tableoptions='') { - if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); - return array(); - } + + + $tabname = $this->connection->getMetaCasedValue($tabname); + $flds = $this->connection->getMetaCasedValue($flds); + + if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_NATIVE ) + { + /* + * METACASE_NATIVE + */ + $tabname = $this->connection->nameQuote . $tabname . $this->connection->nameQuote; + $flds = $this->connection->nameQuote . $flds . $this->connection->nameQuote; + } + $sql = sprintf($this->dropCol,$tabname,$flds); + return (array)$sql; + } + - function ChangeTableSQL($tablename, $flds, $tableoptions = false) + function changeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFields=false) { /** @@ -86,17 +135,25 @@ class ADODB2_db2 extends ADODB_DataDict { $validTypes = array("CHAR","VARC"); $invalidTypes = array("BIGI","BLOB","CLOB","DATE", "DECI","DOUB", "INTE", "REAL","SMAL", "TIME"); // check table exists - $cols = $this->MetaColumns($tablename); + + + $cols = $this->metaColumns($tablename); if ( empty($cols)) { - return $this->CreateTableSQL($tablename, $flds, $tableoptions); + return $this->createTableSQL($tablename, $flds, $tableoptions); } // already exists, alter table instead list($lines,$pkey) = $this->_GenFields($flds); - $alter = 'ALTER TABLE ' . $this->TableName($tablename); + $alter = 'ALTER TABLE ' . $this->tableName($tablename); $sql = array(); foreach ( $lines as $id => $v ) { + /* + * If the metaCasing was NATIVE the col returned with nameQuotes + * around the field. We need to remove this for the metaColumn + * match + */ + $id = str_replace($this->connection->nameQuote,'',$id); if ( isset($cols[$id]) && is_object($cols[$id]) ) { /** If the first field of $v is the fieldname, and diff --git a/datadict/datadict-firebird.inc.php b/datadict/datadict-firebird.inc.php index 77967a6f..548a172b 100644 --- a/datadict/datadict-firebird.inc.php +++ b/datadict/datadict-firebird.inc.php @@ -12,22 +12,31 @@ */ +// security - hide paths +if (!defined('ADODB_DIR')) die(); + class ADODB2_firebird extends ADODB_DataDict { var $databaseType = 'firebird'; var $seqField = false; - var $seqPrefix = 'gen_'; + var $seqPrefix = 's_'; var $blobSize = 40000; + var $renameColumn = 'ALTER TABLE %s ALTER %s TO %s'; + var $alterCol = ' ALTER'; + var $dropCol = ' DROP'; - function ActualType($meta) + function ActualType($meta) { switch($meta) { case 'C': return 'VARCHAR'; - case 'XL': return 'VARCHAR(32000)'; - case 'X': return 'VARCHAR(4000)'; + case 'XL': + case 'X': return 'BLOB SUB_TYPE TEXT'; + + case 'C2': return 'VARCHAR(32765)'; // up to 32K + case 'X2': return 'VARCHAR(4096)'; - case 'C2': return 'VARCHAR'; // up to 32K - case 'X2': return 'VARCHAR(4000)'; + case 'V': return 'CHAR'; + case 'C1': return 'CHAR(1)'; case 'B': return 'BLOB'; @@ -40,7 +49,7 @@ class ADODB2_firebird extends ADODB_DataDict { case 'I1': return 'SMALLINT'; case 'I2': return 'SMALLINT'; case 'I4': return 'INTEGER'; - case 'I8': return 'INTEGER'; + case 'I8': return 'BIGINT'; case 'F': return 'DOUBLE PRECISION'; case 'N': return 'DECIMAL'; @@ -49,7 +58,7 @@ class ADODB2_firebird extends ADODB_DataDict { } } - function NameQuote($name = NULL) + function NameQuote($name = NULL,$allowBrackets=false) { if (!is_string($name)) { return FALSE; @@ -90,9 +99,9 @@ class ADODB2_firebird extends ADODB_DataDict { { if (strpos($t,'.') !== false) { $tarr = explode('.',$t); - return 'DROP GENERATOR '.$tarr[0].'."gen_'.$tarr[1].'"'; + return 'DROP GENERATOR '.$tarr[0].'."s_'.$tarr[1].'"'; } - return 'DROP GENERATOR "GEN_'.$t; + return 'DROP GENERATOR s_'.$t; } @@ -103,11 +112,41 @@ class ADODB2_firebird extends ADODB_DataDict { if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fnotnull) $suffix .= ' NOT NULL'; if ($fautoinc) $this->seqField = $fname; + $fconstraint = preg_replace("/``/", "\"", $fconstraint); if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } + /** + Generate the SQL to create table. Returns an array of sql strings. + */ + function CreateTableSQL($tabname, $flds, $tableoptions=array()) + { + list($lines,$pkey,$idxs) = $this->_GenFields($flds, true); + // genfields can return FALSE at times + if ($lines == null) $lines = array(); + + $taboptions = $this->_Options($tableoptions); + $tabname = $this->TableName ($tabname); + $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions); + + if ($this->autoIncrement && !isset($taboptions['DROP'])) + { $tsql = $this->_Triggers($tabname,$taboptions); + foreach($tsql as $s) $sql[] = $s; + } + + if (is_array($idxs)) { + foreach($idxs as $idx => $idxdef) { + $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); + $sql = array_merge($sql, $sql_idxs); + } + } + + return $sql; + } + + /* CREATE or replace TRIGGER jaddress_insert before insert on jaddress @@ -128,24 +167,60 @@ end; else $tab = $tab1; $seqField = $this->seqField; $seqname = $this->schema.'.'.$this->seqPrefix.$tab; - $trigname = $this->schema.'.trig_'.$this->seqPrefix.$tab; + $trigname = $this->schema.'.t_'.$this->seqPrefix.$tab; } else { $seqField = $this->seqField; $seqname = $this->seqPrefix.$tab1; - $trigname = 'trig_'.$seqname; + $trigname = 't_'.$seqname; + } + + if (isset($tableoptions['DROP'])) + { $sql[] = "DROP GENERATOR $seqname"; } - if (isset($tableoptions['REPLACE'])) + elseif (isset($tableoptions['REPLACE'])) { $sql[] = "DROP GENERATOR \"$seqname\""; $sql[] = "CREATE GENERATOR \"$seqname\""; $sql[] = "ALTER TRIGGER \"$trigname\" BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END"; } else - { $sql[] = "CREATE GENERATOR \"$seqname\""; - $sql[] = "CREATE TRIGGER \"$trigname\" FOR $tabname BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END"; + { $sql[] = "CREATE GENERATOR $seqname"; + $sql[] = "CREATE TRIGGER $trigname FOR $tabname BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID($seqname, 1); END"; } $this->seqField = false; return $sql; } + /** + * Change the definition of one column + * + * As some DBM's can't do that on there own, you need to supply the complete definition of the new table, + * to allow, recreating the table and copying the content over to the new table + * @param string $tabname table-name + * @param string $flds column-name and type for the changed column + * @param string $tableflds='' complete definition of the new table, eg. for postgres, default '' + * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default '' + * @return array with SQL strings + */ + function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + { + $tabname = $this->TableName ($tabname); + $sql = array(); + list($lines,$pkey,$idxs) = $this->_GenFields($flds); + // genfields can return FALSE at times + if ($lines == null) $lines = array(); + $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' '; + foreach($lines as $v) { + $sql[] = $alter . $v; + } + if (is_array($idxs)) { + foreach($idxs as $idx => $idxdef) { + $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']); + $sql = array_merge($sql, $sql_idxs); + } + + } + return $sql; + } + } diff --git a/datadict/datadict-mssql.inc.php b/datadict/datadict-mssql.inc.php index c5fd167a..86ac66ba 100644 --- a/datadict/datadict-mssql.inc.php +++ b/datadict/datadict-mssql.inc.php @@ -269,7 +269,7 @@ CREATE TABLE } - function _GetSize($ftype, $ty, $fsize, $fprec) + function _GetSize($ftype, $ty, $fsize, $fprec, $options=false) { switch ($ftype) { case 'INT': @@ -279,7 +279,7 @@ CREATE TABLE return $ftype; } if ($ty == 'T') return $ftype; - return parent::_GetSize($ftype, $ty, $fsize, $fprec); + return parent::_GetSize($ftype, $ty, $fsize, $fprec, $options); } } diff --git a/datadict/datadict-mssqlnative.inc.php b/datadict/datadict-mssqlnative.inc.php index 0843450c..d85dfb0b 100644 --- a/datadict/datadict-mssqlnative.inc.php +++ b/datadict/datadict-mssqlnative.inc.php @@ -53,6 +53,9 @@ class ADODB2_mssqlnative extends ADODB_DataDict { //var $alterCol = ' ALTER COLUMN '; + public $blobAllowsDefaultValue = true; + public $blobAllowsNotNull = true; + function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { @@ -94,7 +97,10 @@ class ADODB2_mssqlnative extends ADODB_DataDict { -3 => 'X' ); - return $_typeConversion($t); + if (isset($_typeConversion[$t])) + return $_typeConversion[$t]; + + return ADODB_DEFAULT_METATYPE; } @@ -126,7 +132,6 @@ class ADODB2_mssqlnative extends ADODB_DataDict { case 'F': return 'REAL'; case 'N': return 'NUMERIC'; default: - print "RETURN $meta"; return $meta; } } @@ -353,7 +358,7 @@ CREATE TABLE } - function _GetSize($ftype, $ty, $fsize, $fprec) + function _GetSize($ftype, $ty, $fsize, $fprec,$options=false) { switch ($ftype) { case 'INT': @@ -363,7 +368,7 @@ CREATE TABLE return $ftype; } if ($ty == 'T') return $ftype; - return parent::_GetSize($ftype, $ty, $fsize, $fprec); + return parent::_GetSize($ftype, $ty, $fsize, $fprec, $options); } } diff --git a/datadict/datadict-mysql.inc.php b/datadict/datadict-mysql.inc.php index def13030..5ae33b1d 100644 --- a/datadict/datadict-mysql.inc.php +++ b/datadict/datadict-mysql.inc.php @@ -24,8 +24,11 @@ class ADODB2_mysql extends ADODB_DataDict { var $dropIndex = 'DROP INDEX %s ON %s'; var $renameColumn = 'ALTER TABLE %s CHANGE COLUMN %s %s %s'; // needs column-definition! + public $blobAllowsNotNull = true; + function MetaType($t,$len=-1,$fieldobj=false) { + if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; @@ -74,7 +77,7 @@ class ADODB2_mysql extends ADODB_DataDict { case 'SMALLINT': return $is_serial ? 'R' : 'I2'; case 'MEDIUMINT': return $is_serial ? 'R' : 'I4'; case 'BIGINT': return $is_serial ? 'R' : 'I8'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } diff --git a/datadict/datadict-oci8.inc.php b/datadict/datadict-oci8.inc.php index 6a32fc66..9aeafb77 100644 --- a/datadict/datadict-oci8.inc.php +++ b/datadict/datadict-oci8.inc.php @@ -25,8 +25,18 @@ class ADODB2_oci8 extends ADODB_DataDict { var $alterCol = ' MODIFY '; var $typeX = 'VARCHAR(4000)'; var $typeXL = 'CLOB'; + + /** + * Legacy compatibility for sequence names for emulated auto-increments. + * + * If set to true, creates sequences and triggers as TRIG_394545594 + * instead of TRIG_possibly_too_long_tablename + * + * @var bool $useCompactAutoIncrements + */ + public $useCompactAutoIncrements = false; - function MetaType($t, $len=-1, $fieldobj=false) + function metaType($t, $len=-1, $fieldobj=false) { if (is_object($t)) { $fieldobj = $t; @@ -69,7 +79,7 @@ class ADODB2_oci8 extends ADODB_DataDict { return 'I'; default: - return 'N'; + return ADODB_DEFAULT_METATYPE; } } @@ -185,32 +195,52 @@ class ADODB2_oci8 extends ADODB_DataDict { return $suffix; } -/* -CREATE or replace TRIGGER jaddress_insert -before insert on jaddress -for each row -begin -select seqaddress.nextval into :new.A_ID from dual; -end; -*/ + /** + * Creates an insert trigger to emulate an auto-increment column + * in a table + * + * @param string $tabname The name of the table + * @param string[] $tableoptions Optional configuration items + * + * @return string[] The SQL statements to create the trigger + */ function _Triggers($tabname,$tableoptions) { + if (!$this->seqField) return array(); - if ($this->schema) { + if ($this->schema) + { $t = strpos($tabname,'.'); - if ($t !== false) $tab = substr($tabname,$t+1); - else $tab = $tabname; + if ($t !== false) + $tab = substr($tabname,$t+1); + else + $tab = $tabname; + + if ($this->connection->useCompactAutoIncrements) + $id = sprintf('%u',crc32(strtolower($tab))); + else + $id = $tab; + $seqname = $this->schema.'.'.$this->seqPrefix.$tab; $trigname = $this->schema.'.'.$this->trigPrefix.$this->seqPrefix.$tab; - } else { - $seqname = $this->seqPrefix.$tabname; - $trigname = $this->trigPrefix.$seqname; + + } + else + { + if ($this->connection->useCompactAutoIncrements) + $id = sprintf('%u',crc32(strtolower($tabname))); + else + $id = $tabname; + + $seqname = $this->seqPrefix.$id; + $trigname = $this->trigPrefix.$id; } if (strlen($seqname) > 30) { $seqname = $this->seqPrefix.uniqid(''); } // end if + if (strlen($trigname) > 30) { $trigname = $this->trigPrefix.uniqid(''); } // end if @@ -221,8 +251,8 @@ end; $seqIncr = ''; if (isset($tableoptions['SEQUENCE_INCREMENT'])){$seqIncr = ' INCREMENT BY '.$tableoptions['SEQUENCE_INCREMENT'];} $seqStart = ''; - if (isset($tableoptions['SEQUENCE_START'])){$seqIncr = ' START WITH '.$tableoptions['SEQUENCE_START'];} - $sql[] = "CREATE SEQUENCE $seqname $seqStart $seqIncr $seqCache"; + if (isset($tableoptions['SEQUENCE_START'])){$seqStart = ' START WITH '.$tableoptions['SEQUENCE_START'];} + $sql[] = "CREATE SEQUENCE $seqname MINVALUE 1 $seqStart $seqIncr $seqCache"; $sql[] = "CREATE OR REPLACE TRIGGER $trigname BEFORE insert ON $tabname FOR EACH ROW WHEN (NEW.$this->seqField IS NULL OR NEW.$this->seqField = 0) BEGIN select $seqname.nextval into :new.$this->seqField from dual; END;"; $this->seqField = false; diff --git a/datadict/datadict-postgres.inc.php b/datadict/datadict-postgres.inc.php index dd916cab..68bdeddc 100644 --- a/datadict/datadict-postgres.inc.php +++ b/datadict/datadict-postgres.inc.php @@ -25,6 +25,9 @@ class ADODB2_postgres extends ADODB_DataDict { var $renameTable = 'ALTER TABLE %s RENAME TO %s'; // at least since 7.1 var $dropTable = 'DROP TABLE %s CASCADE'; + public $blobAllowsDefaultValue = true; + public $blobAllowsNotNull = true; + function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { @@ -85,7 +88,7 @@ class ADODB2_postgres extends ADODB_DataDict { return 'F'; default: - return 'N'; + return ADODB_DEFAULT_METATYPE; } } @@ -164,11 +167,11 @@ class ADODB2_postgres extends ADODB_DataDict { /** * Change the definition of one column * - * Postgres can't do that on it's own, you need to supply the complete defintion of the new table, + * Postgres can't do that on it's own, you need to supply the complete definition of the new table, * to allow, recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column - * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' + * @param string $tableflds complete definition of the new table, eg. for postgres, default '' * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ @@ -200,7 +203,7 @@ class ADODB2_postgres extends ADODB_DataDict { // this next block doesn't work - there is no way that I can see to // explicitly ask a column to be null using $flds else if ($set_null = preg_match('/NULL/i',$v)) { - // if they didn't specify not null, see if they explicitely asked for null + // if they didn't specify not null, see if they explicitly asked for null // Lookbehind pattern covers the case 'fieldname NULL datatype DEFAULT NULL' // only the first NULL should be removed, not the one specifying // the default value @@ -274,11 +277,11 @@ class ADODB2_postgres extends ADODB_DataDict { /** * Drop one column * - * Postgres < 7.3 can't do that on it's own, you need to supply the complete defintion of the new table, + * Postgres < 7.3 can't do that on it's own, you need to supply the complete definition of the new table, * to allow, recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column - * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' + * @param string $tableflds complete definition of the new table, eg. for postgres, default '' * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ @@ -303,7 +306,7 @@ class ADODB2_postgres extends ADODB_DataDict { * @internal * @param string $tabname table-name * @param string $dropflds column-names to drop - * @param string $tableflds complete defintion of the new table, eg. for postgres + * @param string $tableflds complete definition of the new table, eg. for postgres * @param array/string $tableoptions options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ @@ -312,20 +315,34 @@ class ADODB2_postgres extends ADODB_DataDict { if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); $copyflds = array(); foreach($this->MetaColumns($tabname) as $fld) { - if (!$dropflds || !in_array($fld->name,$dropflds)) { - // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one - if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) && - in_array($fld->type,array('varchar','char','text','bytea'))) { + if (preg_match('/'.$fld->name.' (\w+)/i', $tableflds, $matches)) { + $new_type = strtoupper($matches[1]); + // AlterColumn of a char column to a nummeric one needs an explicit conversation + if (in_array($new_type, array('I', 'I2', 'I4', 'I8', 'N', 'F')) && + in_array($fld->type, array('varchar','char','text','bytea')) + ) { $copyflds[] = "to_number($fld->name,'S9999999999999D99')"; } else { - $copyflds[] = $fld->name; - } - // identify the sequence name and the fld its on - if ($fld->primary_key && $fld->has_default && - preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) { - $seq_name = $matches[1]; - $seq_fld = $fld->name; + // other column-type changes needs explicit decode, encode for bytea or cast otherwise + $new_actual_type = $this->ActualType($new_type); + if (strtoupper($fld->type) != $new_actual_type) { + if ($new_actual_type == 'BYTEA' && $fld->type == 'text') { + $copyflds[] = "DECODE($fld->name, 'escape')"; + } elseif ($fld->type == 'bytea' && $new_actual_type == 'TEXT') { + $copyflds[] = "ENCODE($fld->name, 'escape')"; + } else { + $copyflds[] = "CAST($fld->name AS $new_actual_type)"; + } + } } + } else { + $copyflds[] = $fld->name; + } + // identify the sequence name and the fld its on + if ($fld->primary_key && $fld->has_default && + preg_match("/nextval\('([^']+)'::(text|regclass)\)/",$fld->default_value,$matches)) { + $seq_name = $matches[1]; + $seq_fld = $fld->name; } } $copyflds = implode(', ',$copyflds); @@ -341,7 +358,7 @@ class ADODB2_postgres extends ADODB_DataDict { $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname"; } $aSql[] = "DROP TABLE $tempname"; - // recreate the indexes, if they not contain one of the droped columns + // recreate the indexes, if they not contain one of the dropped columns foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) { if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { @@ -377,7 +394,7 @@ class ADODB2_postgres extends ADODB_DataDict { return $suffix; } - // search for a sequece for the given table (asumes the seqence-name contains the table-name!) + // search for a sequence for the given table (asumes the seqence-name contains the table-name!) // if yes return sql to drop it // this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!! function _DropAutoIncrement($tabname) @@ -386,7 +403,7 @@ class ADODB2_postgres extends ADODB_DataDict { $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'"); - // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly + // check if a tables depends on the sequence and it therefore can't and don't need to be dropped separately if (!$seq || $this->connection->GetOne("SELECT relname FROM pg_class JOIN pg_depend ON pg_class.relfilenode=pg_depend.objid WHERE relname='$seq' AND relkind='S' AND deptype='i'")) { return False; } @@ -472,13 +489,31 @@ CREATE [ UNIQUE ] INDEX index_name ON table return $sql; } - function _GetSize($ftype, $ty, $fsize, $fprec) + function _GetSize($ftype, $ty, $fsize, $fprec, $options=false) { if (strlen($fsize) && $ty != 'X' && $ty != 'B' && $ty != 'I' && strpos($ftype,'(') === false) { $ftype .= "(".$fsize; if (strlen($fprec)) $ftype .= ",".$fprec; $ftype .= ')'; } + + /* + * Handle additional options + */ + if (is_array($options)) + { + foreach($options as $type=>$value) + { + switch ($type) + { + case 'ENUM': + $ftype .= '(' . $value . ')'; + break; + + default: + } + } + } return $ftype; } } diff --git a/datadict/datadict-sapdb.inc.php b/datadict/datadict-sapdb.inc.php index 687f9a52..b169e924 100644 --- a/datadict/datadict-sapdb.inc.php +++ b/datadict/datadict-sapdb.inc.php @@ -71,7 +71,7 @@ class ADODB2_sapdb extends ADODB_DataDict { 'FLOAT' => 'F', 'FIXED' => 'N', ); - $type = isset($maxdb_type2adodb[$t]) ? $maxdb_type2adodb[$t] : 'C'; + $type = isset($maxdb_type2adodb[$t]) ? $maxdb_type2adodb[$t] : ADODB_DEFAULT_METATYPE; // convert integer-types simulated with fixed back to integer if ($t == 'FIXED' && !$fieldobj->scale && ($len == 20 || $len == 3)) { diff --git a/datadict/datadict-sqlite.inc.php b/datadict/datadict-sqlite.inc.php index 90651255..52c031ca 100644 --- a/datadict/datadict-sqlite.inc.php +++ b/datadict/datadict-sqlite.inc.php @@ -25,8 +25,9 @@ class ADODB2_sqlite extends ADODB_DataDict { var $dropIndex = 'DROP INDEX IF EXISTS %s'; var $renameTable = 'ALTER TABLE %s RENAME TO %s'; - - + public $blobAllowsDefaultValue = true; + public $blobAllowsNotNull = true; + function ActualType($meta) { switch(strtoupper($meta)) { @@ -58,7 +59,7 @@ class ADODB2_sqlite extends ADODB_DataDict { } // return string must begin with space - function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) + function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) { $suffix = ''; if ($funsigned) $suffix .= ' UNSIGNED'; diff --git a/docs/README.md b/docs/README.md index b0ec2941..cc36d6c0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # ADOdb Documentation -ADOdb documentation is available in the following locations +ADOdb documentation is available in the following locations: - [Online](http://adodb.org/) - [Download](https://sourceforge.net/projects/adodb/files/Documentation/) for offline use diff --git a/docs/changelog.md b/docs/changelog.md index 22c752ac..474cdf77 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,98 @@ Older changelogs: ## 5.21.0 - Unreleased +- adodb: Remove useless constructors. #171 +- adodb: Define default constructor in ADOConnection base class. #172 +- adodb: Reimplement base methods charMax() and textMax(). #183 +- adodb: addColumnSQL datadict function now supports ENUM data types. See #26 +- adodb: introduce user-defined default Metatype. #165 +- adodb: AutoExecute validates empty fields array. #154 +- adodb: fix getAssoc(). #189, #198, #204 +- adodb: Improve array identification in ADOrecordset::getAssoc(). #101 +- adodb: MetaColumns() consistently returns Actual Type by default in all drivers. #184, #133 +- adodb: Add new value defaulting mode for getInsertSQL(). #214 +- adodb: Added portable substring method. #219 +- adodb: New helper methods: day(), month(), year(). #225 +- adodb: Remove references to obsolete ADOdb Extension. #270 +- adodb: getAssoc() should not change case of result set's outermost key. #335 +- adodb: getAssoc() fix fetch mode. #350 +- adodb: Replace each() with foreach (PHP 7.2 compatibility). #373 +- adodb: Optimize FieldTypesArray with static variable #367 +- adodb: Allow output handler to be callable #312 +- adodb: prevent SQL injection in SelectLimit() #311 +- adodb: fix ADORecordSet constructor signature. #278 +- adodb: add Occitan (#285) and Indonesian (#293) translations. +- adodb: support use of spaces and reserved keywords in replace function. #390 +- adodb: fix adodb_strip_order_by() to only remove the last order by statement. #549 +- adodb: add control over BLOB data dictionary feature (NOT NULL, DEFAULT). #292, #478 +- adodb: fix field names quoting when setting value to null. #572 +- adodb: Remove unneeded ADODB_str_replace function. #582 +- adodb: Remove useless PHP 4 and 5 version checks. #583, #584 +- adodb: replace _array_change_key_case() by internal PHP function. #587 +- adodb: fix getAssoc returning key as value column with ADODB_FETCH_BOTH mode. #600 +- adodb-time: Fix 'Q' (quarter of year) format in adodb_date(). #222 +- adodb-time: Add 'W' (week of year) format support in adodb_date(). #223 +- active record: honor column and table name quoting. #309 +- db2: fix ChangeTableSQL() signature. #338 +- db2: full driver rewrite. #442 +- firebird: updated driver, thanks to Lester Caine. #201 +- mssql: Add charMax() and textMax() methods. #220 +- mssql: Add Convert on SQLDate Method. #304 +- mssql: support alternative port in connect. #314 +- mssql: support Windows authentication. #353 +- mssql: MetaForeignKeys() not returning all FKs. #486 +- mssql: support for T-SQL-style square brackets. #246 +- mssqlnative: Query not returning id. #185 +- mssqlnative: add support for 'l' (day of week) format in sqlDate(). #232 +- mssqlnative: fix invalid return value for ErrorNo(). #298 +- mssqlnative: ensure that the bind array is numeric. #336 +- mssqlnative: support metaProcedures() method #578 +- mssqlnative: fix crash with driver version 5.6 on queries returning no data. #492 +- mysql: setConnectionParameter() now allows multiple parameters with the same key value. #187 +- mysql: prevent use of driver with PHP >= 7.0. #310 +- mysqli: Deprecate $optionFlags property in favor of standard setConnectionParameter() method. #188 +- mysqli: Insert_ID() did not return correct value after executing stored procedure. #166 +- mysqli: method failed if $associative set true. #181 +- mysqli: return fields as ADOFieldObject objects. #175 +- mysqli: support SSL connections. #416 +- mysqli (perf): tables() method definition inconsistent with parent. #435, #462 +- mysql: genId() not returning next sequence value. #493 +- oci8: fix syntax error preventing sequence creation. #540 +- oci8: remove use of curly braces in string offsets (deprecated in PHP 7.4). #570 +- oci8: provide option to create compact trigger/sequence names. #565 +- odbc/mssql: fix null strings concatenation issue with SQL server 2012. #148 +- odbc/mssql: add missing Concat() method. #402 +- odbc: MetaColumns() can optionally be set to return MetaType for backwards compatibility. #184 +- pdo: allow loading of subclassed recordset. #245 +- pdo: add setConnectionParameter support. #247 +- pdo: fix PHP notice. #248 +- pdo: ADORecordSet class loading. #250 +- pdo: add meta extension points. #475 +- pdo/dblib: new driver #496 +- pdo/firebird: new driver #378 +- pdo/mysql: add genID() and createSequence() support. #465 +- pdo/pgsql: Add support for transactions. #363 +- pdo/sqlsrv: fix fetchField() method. #251, #234 +- pdo/sqlsrv: add SetTransactionMode() method. #362 +- pgsql: add CIDR data type to MetaType(). #281 +- pgsql: optimize version check. #334 +- pgsql: fix param number reset with param(false). #380 +- pgsql: use postgres9 driver by default. #474 +- pgsql: specialized casts for _recreate_copy_table(). #207 +- proxy: the client driver and server.php script are deprecated. #444 +- sqlite: _createSuffix is now compatible with parent. #178 +- sqlite: metaIndexes could not locate indexes on uppercase table name. #176 +- sqlite: Fix Metataypes mapping. #177 +- sqlite: driver did not support metaForeignKeys. #179 +- sqlite: metaIndexes() returns column as array instead of CSV. #567 +- memcache: add support for memcached PECL library. #322 +- session: add 'httponly' flag to cookie. #190 +- session: string parameters for `assert` are deprecated in PHP 7.2. #438 +- xml: support table 'opt' attribute with mysqli. #267 +- xml: add support for 'DESCR' tags for tables/fields. #265 +- xml: fix invalid xmlschema03.dtd and descr tag in session schema XML. #595 +- loadbalancer (new feature): read/write splitting and load balancing across multiple connections, thanks to Mike Benoit. #111 + ## 5.20.18 - 28-Jun-2020 - mssql: Retrieve error messages early before connection closed. #614 @@ -82,7 +174,7 @@ Please use version 5.20.12 or later. ## 5.20.8 - 17-Dec-2016 -- mssql: support MSSQL Server 2016 and later #294 +- mssql: support MSSQL Server 2014 and later. #186 #294 - mssql: fix Find() returning no results. #298 - mssql: fix Sequence name forced to 'adodbseq'. #295, #300 - mssql: fix GenId() not returning next sequence value with SQL Server 2005/2008. #302 @@ -228,7 +320,7 @@ other database types as well; all drivers derived from the above are also impact - odbc: clear fields before fetching. See http://phplens.com/lens/lensforum/msgs.php?id=17539 - oci8: GetRowAssoc now works in ADODB_FETCH_ASSOC fetch mode - oci8: MetaType and MetaForeignKeys argument count are now strict-standards compliant -- oci8: Added trailing `;` on trigger creation for sequence fields, prevents occurence of ORA-24344 +- oci8: Added trailing `;` on trigger creation for sequence fields, prevents occurrence of ORA-24344 - oci8quercus: new oci8 driver with support for quercus jdbc data types. - pdo: Fixed concat recursion bug in 5.3. See http://phplens.com/lens/lensforum/msgs.php?id=19285 - pgsql: Default driver (postgres/pgsql) is now postgres8 diff --git a/docs/changelog_v2.x.md b/docs/changelog_v2.x.md index 21db47b3..753322a5 100644 --- a/docs/changelog_v2.x.md +++ b/docs/changelog_v2.x.md @@ -31,7 +31,7 @@ $conn->Connect($dsn); - Now MetaType() can accept a field object as the first parameter. - New $arr = $db->ServerInfo( ) function. Returns $arr['description'] which is the string description, and $arr['version']. - PostgreSQL and MSSQL speedups for insert/updates. -- Implemented new SetFetchMode() that removes the need to use $ADODB_FETCH_MODE. Each connection has independant fetchMode. +- Implemented new SetFetchMode() that removes the need to use $ADODB_FETCH_MODE. Each connection has independent fetchMode. - ADODB_ASSOC_CASE now defaults to 2, use native defaults. This is because we would break backward compat for too many applications otherwise. - Patched encrypted sessions to use replace() - The qstr function supports quoting of nulls when escape character is \ @@ -209,7 +209,7 @@ $conn->Connect($dsn); - Properly implemented Prepare() in oci8 and ODBC. - Added Bind() support to oci8 to support Prepare(). - Improved error handler. Catches CacheExecute() and GenID() errors now. -- Now if you are running php from the command line, debugging messages do not output html formating. Not 100% complete, but getting there. +- Now if you are running php from the command line, debugging messages do not output html formatting. Not 100% complete, but getting there. ## 1.81 - 22 March 2002 @@ -471,7 +471,7 @@ $conn->Connect($dsn); ## 0.71 - 22 Nov 2000 -- Switched from using require_once to include/include_once for backward compatability with PHP 4.02 and earlier. +- Switched from using require_once to include/include_once for backward compatibility with PHP 4.02 and earlier. ## 0.70 - 15 Nov 2000 diff --git a/docs/changelog_v3.x.md b/docs/changelog_v3.x.md index ee2bded4..4b058664 100644 --- a/docs/changelog_v3.x.md +++ b/docs/changelog_v3.x.md @@ -65,7 +65,7 @@ Older changelogs: - Added better debugging for Smart Transactions. - Postgres DBTimeStamp() was wrongly using TO_DATE. Changed to TO_TIMESTAMP. - ADODB_FETCH_CASE check pushed to ADONewConnection to allow people to define it after including adodb.inc.php. -- Added portugese (brazilian) to languages. Thx to "Levi Fukumori". +- Added portuguese (brazilian) to languages. Thx to "Levi Fukumori". - Removed arg3 parameter from Execute/SelectLimit/Cache* functions. - Execute() now accepts 2-d array as $inputarray. Also changed docs of fnExecute() to note change in sql query counting with 2-d arrays. - Added MONEY to MetaType in PostgreSQL. @@ -85,7 +85,7 @@ Older changelogs: - Russian language file contributed by "Cyrill Malevanov" cyrill#malevanov.spb.ru. - Spanish language file contributed by "Horacio Degiorgi" horaciod#codigophp.com. - Error handling in oci8 bugfix - if there was an error in Execute(), then when calling ErrorNo() and/or ErrorMsg(), the 1st call would return the error, but the 2nd call would return no error. -- Error handling in odbc bugfix. ODBC would always return the last error, even if it happened 5 queries ago. Now we reset the errormsg to '' and errorno to 0 everytime before CacheExecute() and Execute(). +- Error handling in odbc bugfix. ODBC would always return the last error, even if it happened 5 queries ago. Now we reset the errormsg to '' and errorno to 0 every time before CacheExecute() and Execute(). ## 3.70 - 29 July 2003 @@ -239,4 +239,4 @@ Older changelogs: - Fixed adodb-pear.inc.php syntax error. - Improved _adodb_getcount() to use SELECT COUNT(*) FROM ($sql) for languages that accept it. - Fixed _adodb_getcount() caching error. -- Added sql to retrive table and column info for odbc_mssql. +- Added sql to retrieve table and column info for odbc_mssql. diff --git a/docs/changelog_v4.x.md b/docs/changelog_v4.x.md index aedd4b64..589c3264 100644 --- a/docs/changelog_v4.x.md +++ b/docs/changelog_v4.x.md @@ -120,7 +120,7 @@ Older changelogs: - Fixed GetActiveRecordsClass() reference bug. See http://phplens.com/lens/lensforum/msgs.php?id=16120 - Added handling of nulls in adodb-ado_mssql.inc.php for qstr(). Thx to Felix Rabinovich. - Adodb-dict contributions by Gaetano - - Support for INDEX in data-dict. Example: idx_ev1. The ability to define indexes using the INDEX keyword was added in ADOdb 4.94. The following example features mutiple indexes, including a compound index idx_ev1. + - Support for INDEX in data-dict. Example: idx_ev1. The ability to define indexes using the INDEX keyword was added in ADOdb 4.94. The following example features multiple indexes, including a compound index idx_ev1. ``` event_id I(11) NOTNULL AUTOINCREMENT PRIMARY, @@ -139,7 +139,7 @@ Older changelogs: - makes any date defined as DEFAULT value for D and T columns work cross-database, not just the "sysdate" value (as long as it is specified using adodb standard format). See above example. - Fixed pdo's GetInsertID() support. Thx Ricky Su. - oci8 Prepare() now sets error messages if an error occurs. -- Added 'PT_BR' to SetDateLocale() -- brazilian portugese. +- Added 'PT_BR' to SetDateLocale() -- brazilian portuguese. - charset in oci8 was not set correctly on `*Connect()` - ADOConnection::Transpose() now appends as first column the field names. - Added $ADODB_QUOTE_FIELDNAMES. If set to true, will autoquote field names in AutoExecute(),GetInsertSQL(), GetUpdateSQL(). @@ -445,7 +445,7 @@ Older changelogs: - MaxDB was padding the defaults of none-string types with spaces - MySql now correctly converts enum columns to varchar - Ralf also changed Postgresql datadict: - - you cant add NOT NULL columns in postgres in one go, they need to be added as NULL and then altered to NOT NULL + - you can't add NOT NULL columns in postgres in one go, they need to be added as NULL and then altered to NOT NULL - AlterColumnSQL could not change a varchar column with numbers into an integer column, postgres need an explicit conversation - a re-created sequence was not set to the correct value, if the name was the old name (no implicit sequence), now always the new name of the implicit sequence is used - Sergio Strampelli added extra $intoken check to Lens_ParseArgs() in datadict code. @@ -520,7 +520,7 @@ Older changelogs: - More XHTML changes for GetMenu. By Jeremy Evans. - Fixes some ibase date issues. Thx to stefan bogdan. - Improvements to mysqli driver to support $ADODB_COUNTRECS. -- Fixed adodb-csvlib.inc.php problem when reading stream from socket. We need to poll stream continiously. +- Fixed adodb-csvlib.inc.php problem when reading stream from socket. We need to poll stream continuously. ## 4.23 - 16 June 2004 @@ -650,7 +650,7 @@ Older changelogs: - CacheSelectLimit internal parameters to SelectLimit were wrong. Thx to Nio. - Modified adodb_pr() and adodb_backtrace() to support command-line usage (eg. no html). - Fixed some fr and it lang errors. Thx to Gaetano G. -- Added contrib directory, with adodb rs to xmlrpc convertor by Gaetano G. +- Added contrib directory, with adodb rs to xmlrpc converter by Gaetano G. - Fixed array recordset bugs when `_skiprow1` is true. Thx to Gaetano G. - Fixed pivot table code when count is false. diff --git a/drivers/adodb-access.inc.php b/drivers/adodb-access.inc.php index 65640ebf..6ca6c675 100644 --- a/drivers/adodb-access.inc.php +++ b/drivers/adodb-access.inc.php @@ -10,15 +10,16 @@ Latest version is available at http://adodb.org/ - Microsoft Access data driver. Requires ODBC. Works only on MS Windows. + Microsoft Access data driver. Requires ODBC. Works only on Microsoft Windows. */ if (!defined('_ADODB_ODBC_LAYER')) { if (!defined('ADODB_DIR')) die(); - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } - if (!defined('_ADODB_ACCESS')) { - define('_ADODB_ACCESS',1); + +if (!defined('_ADODB_ACCESS')) { + define('_ADODB_ACCESS',1); class ADODB_access extends ADODB_odbc { var $databaseType = 'access'; @@ -31,14 +32,6 @@ class ADODB_access extends ADODB_odbc { var $hasTransactions = false; var $upperCase = 'ucase'; - function __construct() - { - global $ADODB_EXTENSION; - - $ADODB_EXTENSION = false; - parent::__construct(); - } - function Time() { return time(); @@ -62,8 +55,6 @@ class ADODB_access extends ADODB_odbc { $ADODB_FETCH_MODE = $savem; if (!$rs) return false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - $arr = $rs->GetArray(); //print_pre($arr); $arr2 = array(); @@ -80,9 +71,6 @@ class ADORecordSet_access extends ADORecordSet_odbc { var $databaseType = "access"; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } -}// class +} // class + } diff --git a/drivers/adodb-ado.inc.php b/drivers/adodb-ado.inc.php index 086b0a0a..1f848f0d 100644 --- a/drivers/adodb-ado.inc.php +++ b/drivers/adodb-ado.inc.php @@ -52,9 +52,7 @@ class ADODB_ado extends ADOConnection { function _affectedrows() { - if (PHP_VERSION >= 5) return $this->_affectedRows; - - return $this->_affectedRows->value; + return $this->_affectedRows; } // you can also pass a connection string like this: @@ -81,7 +79,7 @@ class ADODB_ado extends ADOConnection { // not yet //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename"; - //use trusted conection for SQL if username not specified + //use trusted connection for SQL if username not specified if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes"; } else if ($argProvider=='access') $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider @@ -350,7 +348,7 @@ class ADORecordSet_ado extends ADORecordSet { $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; - return parent::__construct($id,$mode); + parent::__construct($id); } @@ -538,7 +536,7 @@ class ADORecordSet_ado extends ADORecordSet { case 19://adUnsignedInt = 19, case 20://adUnsignedBigInt = 21, return 'I'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } diff --git a/drivers/adodb-ado5.inc.php b/drivers/adodb-ado5.inc.php index dc367413..b95fd97b 100644 --- a/drivers/adodb-ado5.inc.php +++ b/drivers/adodb-ado5.inc.php @@ -52,9 +52,7 @@ class ADODB_ado extends ADOConnection { function _affectedrows() { - if (PHP_VERSION >= 5) return $this->_affectedRows; - - return $this->_affectedRows->value; + return $this->_affectedRows; } // you can also pass a connection string like this: @@ -97,7 +95,7 @@ class ADODB_ado extends ADOConnection { // not yet //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename"; - //use trusted conection for SQL if username not specified + //use trusted connection for SQL if username not specified if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes"; } else if ($argProvider=='access') $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider @@ -384,7 +382,7 @@ class ADORecordSet_ado extends ADORecordSet { $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; - return parent::__construct($id,$mode); + parent::__construct($id); } @@ -579,7 +577,7 @@ class ADORecordSet_ado extends ADORecordSet { case 19://adUnsignedInt = 19, case 20://adUnsignedBigInt = 21, return 'I'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } diff --git a/drivers/adodb-ado_access.inc.php b/drivers/adodb-ado_access.inc.php index 94f4f98a..f76d660c 100644 --- a/drivers/adodb-ado_access.inc.php +++ b/drivers/adodb-ado_access.inc.php @@ -17,8 +17,7 @@ Set tabs to 4 for best viewing. if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ADO_LAYER')) { - if (PHP_VERSION >= 5) include(ADODB_DIR."/drivers/adodb-ado5.inc.php"); - else include(ADODB_DIR."/drivers/adodb-ado.inc.php"); + include_once(ADODB_DIR . "/drivers/adodb-ado5.inc.php"); } class ADODB_ado_access extends ADODB_ado { @@ -43,8 +42,4 @@ class ADORecordSet_ado_access extends ADORecordSet_ado { var $databaseType = "ado_access"; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-ado_mssql.inc.php b/drivers/adodb-ado_mssql.inc.php index 23f174f8..b19211a4 100644 --- a/drivers/adodb-ado_mssql.inc.php +++ b/drivers/adodb-ado_mssql.inc.php @@ -21,8 +21,7 @@ Set tabs to 4 for best viewing. if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ADO_LAYER')) { - if (PHP_VERSION >= 5) include(ADODB_DIR."/drivers/adodb-ado5.inc.php"); - else include(ADODB_DIR."/drivers/adodb-ado.inc.php"); + include_once(ADODB_DIR . "/drivers/adodb-ado5.inc.php"); } @@ -43,12 +42,12 @@ class ADODB_ado_mssql extends ADODB_ado { function _insertid() { - return $this->GetOne('select SCOPE_IDENTITY()'); + return $this->GetOne('select SCOPE_IDENTITY()'); } function _affectedrows() { - return $this->GetOne('select @@rowcount'); + return $this->GetOne('select @@rowcount'); } function SetTransactionMode( $transaction_mode ) @@ -70,32 +69,32 @@ class ADODB_ado_mssql extends ADODB_ado { function MetaColumns($table, $normalize=true) { - $table = strtoupper($table); - $arr= array(); - $dbc = $this->_connectionID; + $table = strtoupper($table); + $arr= array(); + $dbc = $this->_connectionID; - $osoptions = array(); - $osoptions[0] = null; - $osoptions[1] = null; - $osoptions[2] = $table; - $osoptions[3] = null; + $osoptions = array(); + $osoptions[0] = null; + $osoptions[1] = null; + $osoptions[2] = $table; + $osoptions[3] = null; - $adors=@$dbc->OpenSchema(4, $osoptions);//tables + $adors=@$dbc->OpenSchema(4, $osoptions);//tables - if ($adors){ - while (!$adors->EOF){ - $fld = new ADOFieldObject(); - $c = $adors->Fields(3); - $fld->name = $c->Value; - $fld->type = 'CHAR'; // cannot discover type in ADO! - $fld->max_length = -1; - $arr[strtoupper($fld->name)]=$fld; + if ($adors){ + while (!$adors->EOF){ + $fld = new ADOFieldObject(); + $c = $adors->Fields(3); + $fld->name = $c->Value; + $fld->type = 'CHAR'; // cannot discover type in ADO! + $fld->max_length = -1; + $arr[strtoupper($fld->name)]=$fld; - $adors->MoveNext(); - } - $adors->Close(); - } - $false = false; + $adors->MoveNext(); + } + $adors->Close(); + } + $false = false; return empty($arr) ? $false : $arr; } @@ -137,14 +136,10 @@ class ADODB_ado_mssql extends ADODB_ado { //return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'"); } - } // end class +} // end class - class ADORecordSet_ado_mssql extends ADORecordSet_ado { +class ADORecordSet_ado_mssql extends ADORecordSet_ado { var $databaseType = 'ado_mssql'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-ads.inc.php b/drivers/adodb-ads.inc.php index 8d31b219..c87f5e25 100644 --- a/drivers/adodb-ads.inc.php +++ b/drivers/adodb-ads.inc.php @@ -36,601 +36,630 @@ DELPHI FOR PHP USERS: */ // security - hide paths -if (!defined('ADODB_DIR')) die(); +if (!defined('ADODB_DIR')) { + die(); +} - define("_ADODB_ADS_LAYER", 2 ); +define("_ADODB_ADS_LAYER", 2); /*-------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------*/ -class ADODB_ads extends ADOConnection { - var $databaseType = "ads"; - var $fmt = "'m-d-Y'"; - var $fmtTimeStamp = "'Y-m-d H:i:s'"; - var $concat_operator = ''; - var $replaceQuote = "''"; // string to use to replace quotes - var $dataProvider = "ads"; - var $hasAffectedRows = true; - var $binmode = ODBC_BINMODE_RETURN; - var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive - // breaking backward-compat - //var $longreadlen = 8000; // default number of chars to return for a Blob/Long field - var $_bindInputArray = false; - var $curmode = SQL_CUR_USE_DRIVER; // See sqlext.h, SQL_CUR_DEFAULT == SQL_CUR_USE_DRIVER == 2L - var $_genSeqSQL = "create table %s (id integer)"; - var $_autocommit = true; - var $_haserrorfunctions = true; - var $_has_stupid_odbc_fetch_api_change = true; - var $_lastAffectedRows = 0; - var $uCaseTables = true; // for meta* functions, uppercase table names - - - function __construct() - { - $this->_haserrorfunctions = ADODB_PHPVER >= 0x4050; - $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; - } - - // returns true or false - function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - if (!function_exists('ads_connect')) return null; +class ADODB_ads extends ADOConnection +{ + var $databaseType = "ads"; + var $fmt = "'m-d-Y'"; + var $fmtTimeStamp = "'Y-m-d H:i:s'"; + var $concat_operator = ''; + var $replaceQuote = "''"; // string to use to replace quotes + var $dataProvider = "ads"; + var $hasAffectedRows = true; + var $binmode = ODBC_BINMODE_RETURN; + var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive + // breaking backward-compat + //var $longreadlen = 8000; // default number of chars to return for a Blob/Long field + var $_bindInputArray = false; + var $curmode = SQL_CUR_USE_DRIVER; // See sqlext.h, SQL_CUR_DEFAULT == SQL_CUR_USE_DRIVER == 2L + var $_genSeqSQL = "create table %s (id integer)"; + var $_autocommit = true; + var $_lastAffectedRows = 0; + var $uCaseTables = true; // for meta* functions, uppercase table names - if ($this->debug && $argDatabasename && $this->databaseType != 'vfp') { - ADOConnection::outp("For Advantage Connect(), $argDatabasename is not used. Place dsn in 1st parameter."); - } - $last_php_error = $this->resetLastError(); - if ($this->curmode === false) $this->_connectionID = ads_connect($argDSN,$argUsername,$argPassword); - else $this->_connectionID = ads_connect($argDSN,$argUsername,$argPassword,$this->curmode); - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - if (isset($this->connectStmt)) $this->Execute($this->connectStmt); + function __construct() + { + } - return $this->_connectionID != false; - } + // returns true or false + function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + if (!function_exists('ads_connect')) { + return null; + } - // returns true or false - function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - if (!function_exists('ads_connect')) return null; + if ($this->debug && $argDatabasename && $this->databaseType != 'vfp') { + ADOConnection::outp("For Advantage Connect(), $argDatabasename is not used. Place dsn in 1st parameter."); + } + $last_php_error = $this->resetLastError(); + if ($this->curmode === false) { + $this->_connectionID = ads_connect($argDSN, $argUsername, $argPassword); + } else { + $this->_connectionID = ads_connect($argDSN, $argUsername, $argPassword, $this->curmode); + } + $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); + if (isset($this->connectStmt)) { + $this->Execute($this->connectStmt); + } - $last_php_error = $this->resetLastError(); - $this->_errorMsg = ''; - if ($this->debug && $argDatabasename) { - ADOConnection::outp("For PConnect(), $argDatabasename is not used. Place dsn in 1st parameter."); - } - // print "dsn=$argDSN u=$argUsername p=$argPassword<br>"; flush(); - if ($this->curmode === false) $this->_connectionID = ads_connect($argDSN,$argUsername,$argPassword); - else $this->_connectionID = ads_pconnect($argDSN,$argUsername,$argPassword,$this->curmode); + return $this->_connectionID != false; + } - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - if ($this->_connectionID && $this->autoRollback) @ads_rollback($this->_connectionID); - if (isset($this->connectStmt)) $this->Execute($this->connectStmt); + // returns true or false + function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + if (!function_exists('ads_connect')) { + return null; + } - return $this->_connectionID != false; - } + $last_php_error = $this->resetLastError(); + $this->_errorMsg = ''; + if ($this->debug && $argDatabasename) { + ADOConnection::outp("For PConnect(), $argDatabasename is not used. Place dsn in 1st parameter."); + } + // print "dsn=$argDSN u=$argUsername p=$argPassword<br>"; flush(); + if ($this->curmode === false) { + $this->_connectionID = ads_connect($argDSN, $argUsername, $argPassword); + } else { + $this->_connectionID = ads_pconnect($argDSN, $argUsername, $argPassword, $this->curmode); + } - // returns the Server version and Description - function ServerInfo() - { + $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); + if ($this->_connectionID && $this->autoRollback) { + @ads_rollback($this->_connectionID); + } + if (isset($this->connectStmt)) { + $this->Execute($this->connectStmt); + } - if (!empty($this->host) && ADODB_PHPVER >= 0x4300) { - $stmt = $this->Prepare('EXECUTE PROCEDURE sp_mgGetInstallInfo()'); - $res = $this->Execute($stmt); - if(!$res) - print $this->ErrorMsg(); - else{ - $ret["version"]= $res->fields[3]; - $ret["description"]="Advantage Database Server"; - return $ret; - } - } - else { - return ADOConnection::ServerInfo(); - } - } + return $this->_connectionID != false; + } + // returns the Server version and Description + function ServerInfo() + { - // returns true or false - function CreateSequence($seqname = 'adodbseq', $start = 1) - { - $res = $this->Execute("CREATE TABLE $seqname ( ID autoinc( 1 ) ) IN DATABASE"); - if(!$res){ - print $this->ErrorMsg(); - return false; - } - else - return true; + if (!empty($this->host)) { + $stmt = $this->Prepare('EXECUTE PROCEDURE sp_mgGetInstallInfo()'); + $res = $this->Execute($stmt); + if (!$res) { + print $this->ErrorMsg(); + } else { + $ret["version"] = $res->fields[3]; + $ret["description"] = "Advantage Database Server"; + return $ret; + } + } else { + return ADOConnection::ServerInfo(); + } + } - } - // returns true or false - function DropSequence($seqname = 'adodbseq') - { - $res = $this->Execute("DROP TABLE $seqname"); - if(!$res){ - print $this->ErrorMsg(); - return false; - } - else - return true; - } + // returns true or false + function CreateSequence($seqname = 'adodbseq', $start = 1) + { + $res = $this->Execute("CREATE TABLE $seqname ( ID autoinc( 1 ) ) IN DATABASE"); + if (!$res) { + print $this->ErrorMsg(); + return false; + } else { + return true; + } + } - // returns the generated ID or false - // checks if the table already exists, else creates the table and inserts a record into the table - // and gets the ID number of the last inserted record. - function GenID($seqname = 'adodbseq', $start = 1) - { - $go = $this->Execute("select * from $seqname"); - if (!$go){ - $res = $this->Execute("CREATE TABLE $seqname ( ID autoinc( 1 ) ) IN DATABASE"); - if(!res){ - print $this->ErrorMsg(); - return false; - } - } - $res = $this->Execute("INSERT INTO $seqname VALUES( DEFAULT )"); - if(!$res){ - print $this->ErrorMsg(); - return false; - } - else{ - $gen = $this->Execute("SELECT LastAutoInc( STATEMENT ) FROM system.iota"); - $ret = $gen->fields[0]; - return $ret; - } + // returns true or false + function DropSequence($seqname = 'adodbseq') + { + $res = $this->Execute("DROP TABLE $seqname"); + if (!$res) { + print $this->ErrorMsg(); + return false; + } else { + return true; + } + } - } + // returns the generated ID or false + // checks if the table already exists, else creates the table and inserts a record into the table + // and gets the ID number of the last inserted record. + function GenID($seqname = 'adodbseq', $start = 1) + { + $go = $this->Execute("select * from $seqname"); + if (!$go) { + $res = $this->Execute("CREATE TABLE $seqname ( ID autoinc( 1 ) ) IN DATABASE"); + if (!$res) { + print $this->ErrorMsg(); + return false; + } + } + $res = $this->Execute("INSERT INTO $seqname VALUES( DEFAULT )"); + if (!$res) { + print $this->ErrorMsg(); + return false; + } else { + $gen = $this->Execute("SELECT LastAutoInc( STATEMENT ) FROM system.iota"); + $ret = $gen->fields[0]; + return $ret; + } + } - function ErrorMsg() - { - if ($this->_haserrorfunctions) { - if ($this->_errorMsg !== false) return $this->_errorMsg; - if (empty($this->_connectionID)) return @ads_errormsg(); - return @ads_errormsg($this->_connectionID); - } else return ADOConnection::ErrorMsg(); - } + function ErrorMsg() + { + if ($this->_errorMsg !== false) { + return $this->_errorMsg; + } + if (empty($this->_connectionID)) { + return @ads_errormsg(); + } + return @ads_errormsg($this->_connectionID); + } - function ErrorNo() - { + function ErrorNo() + { + if ($this->_errorCode !== false) { + // bug in 4.0.6, error number can be corrupted string (should be 6 digits) + return (strlen($this->_errorCode) <= 2) ? 0 : $this->_errorCode; + } - if ($this->_haserrorfunctions) { - if ($this->_errorCode !== false) { - // bug in 4.0.6, error number can be corrupted string (should be 6 digits) - return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode; - } + if (empty($this->_connectionID)) { + $e = @ads_error(); + } else { + $e = @ads_error($this->_connectionID); + } - if (empty($this->_connectionID)) $e = @ads_error(); - else $e = @ads_error($this->_connectionID); + // bug in 4.0.6, error number can be corrupted string (should be 6 digits) + // so we check and patch + if (strlen($e) <= 2) { + return 0; + } + return $e; + } - // bug in 4.0.6, error number can be corrupted string (should be 6 digits) - // so we check and patch - if (strlen($e)<=2) return 0; - return $e; - } else return ADOConnection::ErrorNo(); - } + function BeginTrans() + { + if (!$this->hasTransactions) { + return false; + } + if ($this->transOff) { + return true; + } + $this->transCnt += 1; + $this->_autocommit = false; + return ads_autocommit($this->_connectionID, false); + } + function CommitTrans($ok = true) + { + if ($this->transOff) { + return true; + } + if (!$ok) { + return $this->RollbackTrans(); + } + if ($this->transCnt) { + $this->transCnt -= 1; + } + $this->_autocommit = true; + $ret = ads_commit($this->_connectionID); + ads_autocommit($this->_connectionID, true); + return $ret; + } - function BeginTrans() - { - if (!$this->hasTransactions) return false; - if ($this->transOff) return true; - $this->transCnt += 1; - $this->_autocommit = false; - return ads_autocommit($this->_connectionID,false); - } + function RollbackTrans() + { + if ($this->transOff) { + return true; + } + if ($this->transCnt) { + $this->transCnt -= 1; + } + $this->_autocommit = true; + $ret = ads_rollback($this->_connectionID); + ads_autocommit($this->_connectionID, true); + return $ret; + } - function CommitTrans($ok=true) - { - if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); - if ($this->transCnt) $this->transCnt -= 1; - $this->_autocommit = true; - $ret = ads_commit($this->_connectionID); - ads_autocommit($this->_connectionID,true); - return $ret; - } - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->_autocommit = true; - $ret = ads_rollback($this->_connectionID); - ads_autocommit($this->_connectionID,true); - return $ret; - } + // Returns tables,Views or both on successful execution. Returns + // tables by default on successful execution. + function &MetaTables($ttype = false, $showSchema = false, $mask = false) + { + $recordSet1 = $this->Execute("select * from system.tables"); + if (!$recordSet1) { + print $this->ErrorMsg(); + return false; + } + $recordSet2 = $this->Execute("select * from system.views"); + if (!$recordSet2) { + print $this->ErrorMsg(); + return false; + } + $i = 0; + while (!$recordSet1->EOF) { + $arr["$i"] = $recordSet1->fields[0]; + $recordSet1->MoveNext(); + $i = $i + 1; + } + if ($ttype == 'FALSE') { + while (!$recordSet2->EOF) { + $arr["$i"] = $recordSet2->fields[0]; + $recordSet2->MoveNext(); + $i = $i + 1; + } + return $arr; + } elseif ($ttype == 'VIEWS') { + while (!$recordSet2->EOF) { + $arrV["$i"] = $recordSet2->fields[0]; + $recordSet2->MoveNext(); + $i = $i + 1; + } + return $arrV; + } else { + return $arr; + } + } - // Returns tables,Views or both on succesfull execution. Returns - // tables by default on succesfull execustion. - function &MetaTables($ttype = false, $showSchema = false, $mask = false) - { - $recordSet1 = $this->Execute("select * from system.tables"); - if(!$recordSet1){ - print $this->ErrorMsg(); - return false; - } - $recordSet2 = $this->Execute("select * from system.views"); - if(!$recordSet2){ - print $this->ErrorMsg(); - return false; - } - $i=0; - while (!$recordSet1->EOF){ - $arr["$i"] = $recordSet1->fields[0]; - $recordSet1->MoveNext(); - $i=$i+1; - } - if($ttype=='FALSE'){ - while (!$recordSet2->EOF){ - $arr["$i"] = $recordSet2->fields[0]; - $recordSet2->MoveNext(); - $i=$i+1; - } - return $arr; - } - elseif($ttype=='VIEWS'){ - while (!$recordSet2->EOF){ - $arrV["$i"] = $recordSet2->fields[0]; - $recordSet2->MoveNext(); - $i=$i+1; - } - return $arrV; - } - else{ - return $arr; - } + function &MetaPrimaryKeys($table, $owner = false) + { + $recordSet = $this->Execute("select table_primary_key from system.tables where name='$table'"); + if (!$recordSet) { + print $this->ErrorMsg(); + return false; + } + $i = 0; + while (!$recordSet->EOF) { + $arr["$i"] = $recordSet->fields[0]; + $recordSet->MoveNext(); + $i = $i + 1; + } + return $arr; + } - } + /* + See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcdatetime_data_type_changes.asp + / SQL data type codes / + #define SQL_UNKNOWN_TYPE 0 + #define SQL_CHAR 1 + #define SQL_NUMERIC 2 + #define SQL_DECIMAL 3 + #define SQL_INTEGER 4 + #define SQL_SMALLINT 5 + #define SQL_FLOAT 6 + #define SQL_REAL 7 + #define SQL_DOUBLE 8 + #if (ODBCVER >= 0x0300) + #define SQL_DATETIME 9 + #endif + #define SQL_VARCHAR 12 - function &MetaPrimaryKeys($table, $owner = false) - { - $recordSet = $this->Execute("select table_primary_key from system.tables where name='$table'"); - if(!$recordSet){ - print $this->ErrorMsg(); - return false; - } - $i=0; - while (!$recordSet->EOF){ - $arr["$i"] = $recordSet->fields[0]; - $recordSet->MoveNext(); - $i=$i+1; - } - return $arr; - } - -/* -See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcdatetime_data_type_changes.asp -/ SQL data type codes / -#define SQL_UNKNOWN_TYPE 0 -#define SQL_CHAR 1 -#define SQL_NUMERIC 2 -#define SQL_DECIMAL 3 -#define SQL_INTEGER 4 -#define SQL_SMALLINT 5 -#define SQL_FLOAT 6 -#define SQL_REAL 7 -#define SQL_DOUBLE 8 -#if (ODBCVER >= 0x0300) -#define SQL_DATETIME 9 -#endif -#define SQL_VARCHAR 12 + / One-parameter shortcuts for date/time data types / + #if (ODBCVER >= 0x0300) + #define SQL_TYPE_DATE 91 + #define SQL_TYPE_TIME 92 + #define SQL_TYPE_TIMESTAMP 93 -/ One-parameter shortcuts for date/time data types / -#if (ODBCVER >= 0x0300) -#define SQL_TYPE_DATE 91 -#define SQL_TYPE_TIME 92 -#define SQL_TYPE_TIMESTAMP 93 - -#define SQL_UNICODE (-95) -#define SQL_UNICODE_VARCHAR (-96) -#define SQL_UNICODE_LONGVARCHAR (-97) -*/ - function ODBCTypes($t) - { - switch ((integer)$t) { - case 1: - case 12: - case 0: - case -95: - case -96: - return 'C'; - case -97: - case -1: //text - return 'X'; - case -4: //image - return 'B'; + #define SQL_UNICODE (-95) + #define SQL_UNICODE_VARCHAR (-96) + #define SQL_UNICODE_LONGVARCHAR (-97) + */ + function ODBCTypes($t) + { + switch ((integer)$t) { + case 1: + case 12: + case 0: + case -95: + case -96: + return 'C'; + case -97: + case -1: //text + return 'X'; + case -4: //image + return 'B'; - case 9: - case 91: - return 'D'; + case 9: + case 91: + return 'D'; - case 10: - case 11: - case 92: - case 93: - return 'T'; + case 10: + case 11: + case 92: + case 93: + return 'T'; - case 4: - case 5: - case -6: - return 'I'; + case 4: + case 5: + case -6: + return 'I'; - case -11: // uniqidentifier - return 'R'; - case -7: //bit - return 'L'; + case -11: // uniqidentifier + return 'R'; + case -7: //bit + return 'L'; - default: - return 'N'; - } - } + default: + return 'N'; + } + } - function &MetaColumns($table, $normalize = true) - { - global $ADODB_FETCH_MODE; + function &MetaColumns($table, $normalize = true) + { + global $ADODB_FETCH_MODE; - $false = false; - if ($this->uCaseTables) $table = strtoupper($table); - $schema = ''; - $this->_findschema($table,$schema); + $false = false; + if ($this->uCaseTables) { + $table = strtoupper($table); + } + $schema = ''; + $this->_findschema($table, $schema); - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - /*if (false) { // after testing, confirmed that the following does not work becoz of a bug - $qid2 = ads_tables($this->_connectionID); - $rs = new ADORecordSet_ads($qid2); - $ADODB_FETCH_MODE = $savem; - if (!$rs) return false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - $rs->_fetch(); + /*if (false) { // after testing, confirmed that the following does not work because of a bug + $qid2 = ads_tables($this->_connectionID); + $rs = new ADORecordSet_ads($qid2); + $ADODB_FETCH_MODE = $savem; + if (!$rs) return false; + $rs->_fetch(); - while (!$rs->EOF) { - if ($table == strtoupper($rs->fields[2])) { - $q = $rs->fields[0]; - $o = $rs->fields[1]; - break; - } - $rs->MoveNext(); - } - $rs->Close(); + while (!$rs->EOF) { + if ($table == strtoupper($rs->fields[2])) { + $q = $rs->fields[0]; + $o = $rs->fields[1]; + break; + } + $rs->MoveNext(); + } + $rs->Close(); - $qid = ads_columns($this->_connectionID,$q,$o,strtoupper($table),'%'); - } */ + $qid = ads_columns($this->_connectionID,$q,$o,strtoupper($table),'%'); + } */ - switch ($this->databaseType) { - case 'access': - case 'vfp': - $qid = ads_columns($this->_connectionID);#,'%','',strtoupper($table),'%'); - break; + switch ($this->databaseType) { + case 'access': + case 'vfp': + $qid = ads_columns($this->_connectionID);#,'%','',strtoupper($table),'%'); + break; - case 'db2': - $colname = "%"; - $qid = ads_columns($this->_connectionID, "", $schema, $table, $colname); - break; + case 'db2': + $colname = "%"; + $qid = ads_columns($this->_connectionID, "", $schema, $table, $colname); + break; - default: - $qid = @ads_columns($this->_connectionID,'%','%',strtoupper($table),'%'); - if (empty($qid)) $qid = ads_columns($this->_connectionID); - break; - } - if (empty($qid)) return $false; + default: + $qid = @ads_columns($this->_connectionID, '%', '%', strtoupper($table), '%'); + if (empty($qid)) { + $qid = ads_columns($this->_connectionID); + } + break; + } + if (empty($qid)) { + return $false; + } - $rs = new ADORecordSet_ads($qid); - $ADODB_FETCH_MODE = $savem; + $rs = new ADORecordSet_ads($qid); + $ADODB_FETCH_MODE = $savem; - if (!$rs) return $false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - $rs->_fetch(); + if (!$rs) { + return $false; + } + $rs->_fetch(); - $retarr = array(); + $retarr = array(); - /* - $rs->fields indices - 0 TABLE_QUALIFIER - 1 TABLE_SCHEM - 2 TABLE_NAME - 3 COLUMN_NAME - 4 DATA_TYPE - 5 TYPE_NAME - 6 PRECISION - 7 LENGTH - 8 SCALE - 9 RADIX - 10 NULLABLE - 11 REMARKS - */ - while (!$rs->EOF) { - // adodb_pr($rs->fields); - if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { - $fld = new ADOFieldObject(); - $fld->name = $rs->fields[3]; - $fld->type = $this->ODBCTypes($rs->fields[4]); + /* + $rs->fields indices + 0 TABLE_QUALIFIER + 1 TABLE_SCHEM + 2 TABLE_NAME + 3 COLUMN_NAME + 4 DATA_TYPE + 5 TYPE_NAME + 6 PRECISION + 7 LENGTH + 8 SCALE + 9 RADIX + 10 NULLABLE + 11 REMARKS + */ + while (!$rs->EOF) { + // adodb_pr($rs->fields); + if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[3]; + $fld->type = $this->ODBCTypes($rs->fields[4]); - // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp - // access uses precision to store length for char/varchar - if ($fld->type == 'C' or $fld->type == 'X') { - if ($this->databaseType == 'access') - $fld->max_length = $rs->fields[6]; - else if ($rs->fields[4] <= -95) // UNICODE - $fld->max_length = $rs->fields[7]/2; - else - $fld->max_length = $rs->fields[7]; - } else - $fld->max_length = $rs->fields[7]; - $fld->not_null = !empty($rs->fields[10]); - $fld->scale = $rs->fields[8]; - $retarr[strtoupper($fld->name)] = $fld; - } else if (sizeof($retarr)>0) - break; - $rs->MoveNext(); - } - $rs->Close(); //-- crashes 4.03pl1 -- why? + // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp + // access uses precision to store length for char/varchar + if ($fld->type == 'C' or $fld->type == 'X') { + if ($this->databaseType == 'access') { + $fld->max_length = $rs->fields[6]; + } else { + if ($rs->fields[4] <= -95) // UNICODE + { + $fld->max_length = $rs->fields[7] / 2; + } else { + $fld->max_length = $rs->fields[7]; + } + } + } else { + $fld->max_length = $rs->fields[7]; + } + $fld->not_null = !empty($rs->fields[10]); + $fld->scale = $rs->fields[8]; + $retarr[strtoupper($fld->name)] = $fld; + } else { + if (sizeof($retarr) > 0) { + break; + } + } + $rs->MoveNext(); + } + $rs->Close(); //-- crashes 4.03pl1 -- why? - if (empty($retarr)) $retarr = false; - return $retarr; - } + if (empty($retarr)) { + $retarr = false; + } + return $retarr; + } - // Returns an array of columns names for a given table - function &MetaColumnNames($table, $numIndexes = false, $useattnum = false) - { - $recordSet = $this->Execute("select name from system.columns where parent='$table'"); - if(!$recordSet){ - print $this->ErrorMsg(); - return false; - } - else{ - $i=0; - while (!$recordSet->EOF){ - $arr["FIELD$i"] = $recordSet->fields[0]; - $recordSet->MoveNext(); - $i=$i+1; - } - return $arr; - } - } + // Returns an array of columns names for a given table + function &MetaColumnNames($table, $numIndexes = false, $useattnum = false) + { + $recordSet = $this->Execute("select name from system.columns where parent='$table'"); + if (!$recordSet) { + print $this->ErrorMsg(); + return false; + } else { + $i = 0; + while (!$recordSet->EOF) { + $arr["FIELD$i"] = $recordSet->fields[0]; + $recordSet->MoveNext(); + $i = $i + 1; + } + return $arr; + } + } - function Prepare($sql) - { - if (! $this->_bindInputArray) return $sql; // no binding - $stmt = ads_prepare($this->_connectionID,$sql); - if (!$stmt) { - // we don't know whether odbc driver is parsing prepared stmts, so just return sql - return $sql; - } - return array($sql,$stmt,false); - } + function Prepare($sql) + { + if (!$this->_bindInputArray) { + return $sql; + } // no binding + $stmt = ads_prepare($this->_connectionID, $sql); + if (!$stmt) { + // we don't know whether odbc driver is parsing prepared stmts, so just return sql + return $sql; + } + return array($sql, $stmt, false); + } - /* returns queryID or false */ - function _query($sql,$inputarr=false) - { - $last_php_error = $this->resetLastError(); - $this->_errorMsg = ''; + /* returns queryID or false */ + function _query($sql, $inputarr = false) + { + $last_php_error = $this->resetLastError(); + $this->_errorMsg = ''; - if ($inputarr) { - if (is_array($sql)) { - $stmtid = $sql[1]; - } else { - $stmtid = ads_prepare($this->_connectionID,$sql); + if ($inputarr) { + if (is_array($sql)) { + $stmtid = $sql[1]; + } else { + $stmtid = ads_prepare($this->_connectionID, $sql); - if ($stmtid == false) { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - return false; - } - } + if ($stmtid == false) { + $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); + return false; + } + } - if (! ads_execute($stmtid,$inputarr)) { - //@ads_free_result($stmtid); - if ($this->_haserrorfunctions) { - $this->_errorMsg = ads_errormsg(); - $this->_errorCode = ads_error(); - } - return false; - } + if (!ads_execute($stmtid, $inputarr)) { + //@ads_free_result($stmtid); + $this->_errorMsg = ads_errormsg(); + $this->_errorCode = ads_error(); + return false; + } - } else if (is_array($sql)) { - $stmtid = $sql[1]; - if (!ads_execute($stmtid)) { - //@ads_free_result($stmtid); - if ($this->_haserrorfunctions) { - $this->_errorMsg = ads_errormsg(); - $this->_errorCode = ads_error(); - } - return false; - } - } else - { + } else { + if (is_array($sql)) { + $stmtid = $sql[1]; + if (!ads_execute($stmtid)) { + //@ads_free_result($stmtid); + $this->_errorMsg = ads_errormsg(); + $this->_errorCode = ads_error(); + return false; + } + } else { - $stmtid = ads_exec($this->_connectionID,$sql); + $stmtid = ads_exec($this->_connectionID, $sql); - } + } + } - $this->_lastAffectedRows = 0; + $this->_lastAffectedRows = 0; - if ($stmtid) { + if ($stmtid) { - if (@ads_num_fields($stmtid) == 0) { - $this->_lastAffectedRows = ads_num_rows($stmtid); - $stmtid = true; + if (@ads_num_fields($stmtid) == 0) { + $this->_lastAffectedRows = ads_num_rows($stmtid); + $stmtid = true; - } else { + } else { - $this->_lastAffectedRows = 0; - ads_binmode($stmtid,$this->binmode); - ads_longreadlen($stmtid,$this->maxblobsize); + $this->_lastAffectedRows = 0; + ads_binmode($stmtid, $this->binmode); + ads_longreadlen($stmtid, $this->maxblobsize); - } + } - if ($this->_haserrorfunctions) { - $this->_errorMsg = ''; - $this->_errorCode = 0; - } else { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - } - } else { - if ($this->_haserrorfunctions) { - $this->_errorMsg = ads_errormsg(); - $this->_errorCode = ads_error(); - } else { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - } - } + $this->_errorMsg = ''; + $this->_errorCode = 0; + } else { + $this->_errorMsg = ads_errormsg(); + $this->_errorCode = ads_error(); + } - return $stmtid; + return $stmtid; - } + } - /* - Insert a null into the blob field of the table first. - Then use UpdateBlob to store the blob. + /* + Insert a null into the blob field of the table first. + Then use UpdateBlob to store the blob. - Usage: + Usage: - $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); - $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); - */ - function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') - { - $last_php_error = $this->resetLastError(); - $sql = "UPDATE $table SET $column=? WHERE $where"; - $stmtid = ads_prepare($this->_connectionID,$sql); - if ($stmtid == false){ - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - return false; - } - if (! ads_execute($stmtid,array($val),array(SQL_BINARY) )){ - if ($this->_haserrorfunctions){ - $this->_errorMsg = ads_errormsg(); - $this->_errorCode = ads_error(); - } - return false; - } - return TRUE; - } + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table, $column, $val, $where, $blobtype = 'BLOB') + { + $last_php_error = $this->resetLastError(); + $sql = "UPDATE $table SET $column=? WHERE $where"; + $stmtid = ads_prepare($this->_connectionID, $sql); + if ($stmtid == false) { + $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); + return false; + } + if (!ads_execute($stmtid, array($val), array(SQL_BINARY))) { + $this->_errorMsg = ads_errormsg(); + $this->_errorCode = ads_error(); + return false; + } + return true; + } - // returns true or false - function _close() - { - $ret = @ads_close($this->_connectionID); - $this->_connectionID = false; - return $ret; - } + // returns true or false + function _close() + { + $ret = @ads_close($this->_connectionID); + $this->_connectionID = false; + return $ret; + } - function _affectedrows() - { - return $this->_lastAffectedRows; - } + function _affectedrows() + { + return $this->_lastAffectedRows; + } } @@ -638,139 +667,142 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/od Class Name: Recordset --------------------------------------------------------------------------------------*/ -class ADORecordSet_ads extends ADORecordSet { +class ADORecordSet_ads extends ADORecordSet +{ - var $bind = false; - var $databaseType = "ads"; - var $dataProvider = "ads"; - var $useFetchArray; - var $_has_stupid_odbc_fetch_api_change; + var $bind = false; + var $databaseType = "ads"; + var $dataProvider = "ads"; + var $useFetchArray; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; + function __construct($id, $mode = false) + { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + $this->fetchMode = $mode; - $this->_queryID = $id; + $this->_queryID = $id; - // the following is required for mysql odbc driver in 4.3.1 -- why? - $this->EOF = false; - $this->_currentRow = -1; - //parent::__construct($id); - } + // the following is required for mysql odbc driver in 4.3.1 -- why? + $this->EOF = false; + $this->_currentRow = -1; + //parent::__construct($id); + } - // returns the field object - function &FetchField($fieldOffset = -1) - { + // returns the field object + function &FetchField($fieldOffset = -1) + { - $off=$fieldOffset+1; // offsets begin at 1 + $off = $fieldOffset + 1; // offsets begin at 1 - $o= new ADOFieldObject(); - $o->name = @ads_field_name($this->_queryID,$off); - $o->type = @ads_field_type($this->_queryID,$off); - $o->max_length = @ads_field_len($this->_queryID,$off); - if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); - else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); - return $o; - } + $o = new ADOFieldObject(); + $o->name = @ads_field_name($this->_queryID, $off); + $o->type = @ads_field_type($this->_queryID, $off); + $o->max_length = @ads_field_len($this->_queryID, $off); + if (ADODB_ASSOC_CASE == 0) { + $o->name = strtolower($o->name); + } else { + if (ADODB_ASSOC_CASE == 1) { + $o->name = strtoupper($o->name); + } + } + return $o; + } - /* Use associative array to get fields array */ - function Fields($colname) - { - if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; - if (!$this->bind) { - $this->bind = array(); - for ($i=0; $i < $this->_numOfFields; $i++) { - $o = $this->FetchField($i); - $this->bind[strtoupper($o->name)] = $i; - } - } + /* Use associative array to get fields array */ + function Fields($colname) + { + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + return $this->fields[$colname]; + } + if (!$this->bind) { + $this->bind = array(); + for ($i = 0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } - return $this->fields[$this->bind[strtoupper($colname)]]; - } + return $this->fields[$this->bind[strtoupper($colname)]]; + } - function _initrs() - { - global $ADODB_COUNTRECS; - $this->_numOfRows = ($ADODB_COUNTRECS) ? @ads_num_rows($this->_queryID) : -1; - $this->_numOfFields = @ads_num_fields($this->_queryID); - // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 - if ($this->_numOfRows == 0) $this->_numOfRows = -1; - //$this->useFetchArray = $this->connection->useFetchArray; - $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; - } + function _initrs() + { + global $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS) ? @ads_num_rows($this->_queryID) : -1; + $this->_numOfFields = @ads_num_fields($this->_queryID); + // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 + if ($this->_numOfRows == 0) { + $this->_numOfRows = -1; + } + //$this->useFetchArray = $this->connection->useFetchArray; + } - function _seek($row) - { - return false; - } + function _seek($row) + { + return false; + } - // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated - function &GetArrayLimit($nrows,$offset=-1) - { - if ($offset <= 0) { - $rs =& $this->GetArray($nrows); - return $rs; - } - $savem = $this->fetchMode; - $this->fetchMode = ADODB_FETCH_NUM; - $this->Move($offset); - $this->fetchMode = $savem; + // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated + function &GetArrayLimit($nrows, $offset = -1) + { + if ($offset <= 0) { + $rs =& $this->GetArray($nrows); + return $rs; + } + $savem = $this->fetchMode; + $this->fetchMode = ADODB_FETCH_NUM; + $this->Move($offset); + $this->fetchMode = $savem; - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields =& $this->GetRowAssoc(); - } + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + $this->fields =& $this->GetRowAssoc(); + } - $results = array(); - $cnt = 0; - while (!$this->EOF && $nrows != $cnt) { - $results[$cnt++] = $this->fields; - $this->MoveNext(); - } + $results = array(); + $cnt = 0; + while (!$this->EOF && $nrows != $cnt) { + $results[$cnt++] = $this->fields; + $this->MoveNext(); + } - return $results; - } + return $results; + } - function MoveNext() - { - if ($this->_numOfRows != 0 && !$this->EOF) { - $this->_currentRow++; - if( $this->_fetch() ) { - return true; - } - } - $this->fields = false; - $this->EOF = true; - return false; - } + function MoveNext() + { + if ($this->_numOfRows != 0 && !$this->EOF) { + $this->_currentRow++; + if ($this->_fetch()) { + return true; + } + } + $this->fields = false; + $this->EOF = true; + return false; + } - function _fetch() - { - $this->fields = false; - if ($this->_has_stupid_odbc_fetch_api_change) - $rez = @ads_fetch_into($this->_queryID,$this->fields); - else { - $row = 0; - $rez = @ads_fetch_into($this->_queryID,$row,$this->fields); - } - if ($rez) { - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields =& $this->GetRowAssoc(); - } - return true; - } - return false; - } + function _fetch() + { + $this->fields = false; + $rez = @ads_fetch_into($this->_queryID, $this->fields); + if ($rez) { + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + $this->fields =& $this->GetRowAssoc(); + } + return true; + } + return false; + } - function _close() - { - return @ads_free_result($this->_queryID); - } + function _close() + { + return @ads_free_result($this->_queryID); + } } diff --git a/drivers/adodb-borland_ibase.inc.php b/drivers/adodb-borland_ibase.inc.php index f2d1952e..ad72d74d 100644 --- a/drivers/adodb-borland_ibase.inc.php +++ b/drivers/adodb-borland_ibase.inc.php @@ -82,8 +82,4 @@ class ADORecordSet_borland_ibase extends ADORecordSet_ibase { var $databaseType = "borland_ibase"; - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-csv.inc.php b/drivers/adodb-csv.inc.php index 2e3c16f5..69de8cbc 100644 --- a/drivers/adodb-csv.inc.php +++ b/drivers/adodb-csv.inc.php @@ -38,18 +38,14 @@ class ADODB_csv extends ADOConnection { var $hasTransactions = false; var $_errorNo = false; - function __construct() - { - } - function _insertid() { - return $this->_insertid; + return $this->_insertid; } function _affectedrows() { - return $this->_affectedrows; + return $this->_affectedrows; } function MetaDatabases() @@ -178,7 +174,7 @@ class ADODB_csv extends ADOConnection { /* Returns: the last error message from previous database operation */ function ErrorMsg() { - return $this->_errorMsg; + return $this->_errorMsg; } /* Returns: the last error number from previous database operation */ @@ -195,10 +191,6 @@ class ADODB_csv extends ADOConnection { } // class class ADORecordset_csv extends ADORecordset { - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } function _close() { diff --git a/drivers/adodb-db2.inc.php b/drivers/adodb-db2.inc.php index a56385d2..2f883df5 100644 --- a/drivers/adodb-db2.inc.php +++ b/drivers/adodb-db2.inc.php @@ -3,18 +3,25 @@ @version v5.21.0-dev ??-???-2016 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved. @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community + Released under both BSD license and Lesser GPL library license. + Whenever there is any discrepancy between the two licenses, + the BSD license will take precedence. - This is a version of the ADODB driver for DB2. It uses the 'ibm_db2' PECL extension - for PHP (http://pecl.php.net/package/ibm_db2), which in turn requires DB2 V8.2.2 or - higher. + Set tabs to 4 for best viewing. - Originally tested with PHP 5.1.1 and Apache 2.0.55 on Windows XP SP2. - More recently tested with PHP 5.1.2 and Apache 2.0.55 on Windows XP SP2. - - This file was ported from "adodb-odbc.inc.php" by Larry Menard, "larry.menard#rogers.com". - I ripped out what I believed to be a lot of redundant or obsolete code, but there are - probably still some remnants of the ODBC support in this file; I'm relying on reviewers - of this code to point out any other things that can be removed. +* Driver for use with IBM DB2 Native Client +* +* Originally DB2 drivers were dependent on an ODBC driver, and some installations +* may still use that. To use an ODBC driver connection, use the odbc_db2 +* ADOdb driver. For Linux, you need the 'ibm_db2' PECL extension for PHP, +* For Windows, you need to locate an appropriate version of the php_ibm_db2.dll, +* as well as the IBM data server client software. +* This is basically a full rewrite of the original driver, for information +* about all the changes, see the update information on the ADOdb website +* for version 5.21.0 +* +* @link http://pecl.php.net/package/ibm_db2 Pecl Extension For DB2 +* @author Mark Newnham */ // security - hide paths @@ -22,11 +29,8 @@ if (!defined('ADODB_DIR')) die(); define("_ADODB_DB2_LAYER", 2 ); -/*-------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------*/ - - - +/*-------------------------------------------------------------------- +----------------------------------------------------------------------*/ class ADODB_db2 extends ADOConnection { @@ -45,101 +49,375 @@ class ADODB_db2 extends ADOConnection { var $binmode = DB2_BINARY; - var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive - // breaking backward-compat - var $_bindInputArray = false; + /* + * setting this to true will make array elements in FETCH_ASSOC + * mode case-sensitive breaking backward-compat + */ + var $useFetchArray = false; + var $_bindInputArray = true; var $_genIDSQL = "VALUES NEXTVAL FOR %s"; - var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s NO MAXVALUE NO CYCLE"; + var $_genSeqSQL = " + CREATE SEQUENCE %s START WITH %s + NO MAXVALUE NO CYCLE INCREMENT BY 1 NO CACHE + "; var $_dropSeqSQL = "DROP SEQUENCE %s"; var $_autocommit = true; - var $_haserrorfunctions = true; var $_lastAffectedRows = 0; - var $uCaseTables = true; // for meta* functions, uppercase table names var $hasInsertID = true; + var $hasGenID = true; + + /* + * Character used to wrap column and table names for escaping special + * characters in column and table names as well as forcing upper and + * lower case + */ + public $nameQuote = '"'; + + /* + * Executed after successful connection + */ + public $connectStmt = ''; + + /* + * Holds the current database name + */ + private $databaseName = ''; + /* + * Holds information about the stored procedure request + * currently being built + */ + private $storedProcedureParameters = false; + + + function __construct() {} function _insertid() { return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()'); } - function __construct() + public function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) { - $this->_haserrorfunctions = ADODB_PHPVER >= 0x4050; + return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename); } - - // returns true or false - function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) + + public function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) { + return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename,true); + } + + private function doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persistent=false) + { + global $php_errormsg; + if (!function_exists('db2_connect')) { - ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension which is not installed."); + ADOConnection::outp("DB2 extension not installed."); + return null; + } + + $connectionParameters = $this->unpackParameters($argDSN, + $argUsername, + $argPassword, + $argDatabasename); + + if ($connectionParameters == null) + { + /* + * Error thrown + */ return null; } - // This needs to be set before the connect(). - // Replaces the odbc_binmode() call that was in Execute() + + $argDSN = $connectionParameters['dsn']; + $argUsername = $connectionParameters['uid']; + $argPassword = $connectionParameters['pwd']; + $argDatabasename = $connectionParameters['database']; + $useCataloguedConnection = $connectionParameters['catalogue']; + + if ($this->debug){ + if ($useCataloguedConnection){ + $connectMessage = "Catalogued connection using parameters: "; + $connectMessage .= "DB=$argDatabasename / "; + $connectMessage .= "UID=$argUsername / "; + $connectMessage .= "PWD=$argPassword"; + } + else + { + $connectMessage = "Uncatalogued connection using DSN: $argDSN"; + } + ADOConnection::outp($connectMessage); + } + /* + * This needs to be set before the connect(). + */ ini_set('ibm_db2.binmode', $this->binmode); - if ($argDatabasename && empty($argDSN)) { + if ($persistent) + $db2Function = 'db2_pconnect'; + else + $db2Function = 'db2_connect'; + + /* + * We need to flatten out the connectionParameters + */ - if (stripos($argDatabasename,'UID=') && stripos($argDatabasename,'PWD=')) $this->_connectionID = db2_connect($argDatabasename,null,null); - else $this->_connectionID = db2_connect($argDatabasename,$argUsername,$argPassword); - } else { - if ($argDatabasename) $schema = $argDatabasename; - if (stripos($argDSN,'UID=') && stripos($argDSN,'PWD=')) $this->_connectionID = db2_connect($argDSN,null,null); - else $this->_connectionID = db2_connect($argDSN,$argUsername,$argPassword); + $db2Options = array(); + if ($this->connectionParameters) + { + foreach($this->connectionParameters as $p) + foreach($p as $k=>$v) + $db2Options[$k] = $v; } - - // For db2_connect(), there is an optional 4th arg. If present, it must be - // an array of valid options. So far, we don't use them. + + if ($useCataloguedConnection) + $this->_connectionID = $db2Function($argDatabasename, + $argUsername, + $argPassword, + $db2Options); + else + $this->_connectionID = $db2Function($argDSN, + null, + null, + $db2Options); + + $php_errormsg = ''; $this->_errorMsg = @db2_conn_errormsg(); - if (isset($this->connectStmt)) $this->Execute($this->connectStmt); + + if ($this->_connectionID && $this->connectStmt) + $this->execute($this->connectStmt); - if ($this->_connectionID && isset($schema)) $this->Execute("SET SCHEMA=$schema"); return $this->_connectionID != false; - } - - // returns true or false - function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + + } + + /** + * Validates and preprocesses the passed parameters for consistency + * + * @param string $argDSN Either DSN or database + * @param string $argUsername User name or null + * @param string $argPassword Password or null + * @param string $argDatabasename Either DSN or database + * + * @return mixed array if correct, null if not + */ + private function unpackParameters($argDSN, $argUsername, $argPassword, $argDatabasename) { - if (!function_exists('db2_connect')) return null; - - // This needs to be set before the connect(). - // Replaces the odbc_binmode() call that was in Execute() - ini_set('ibm_db2.binmode', $this->binmode); - - $this->_errorMsg = ''; - - if ($argDatabasename && empty($argDSN)) { - - if (stripos($argDatabasename,'UID=') && stripos($argDatabasename,'PWD=')) $this->_connectionID = db2_pconnect($argDatabasename,null,null); - else $this->_connectionID = db2_pconnect($argDatabasename,$argUsername,$argPassword); - } else { - if ($argDatabasename) $schema = $argDatabasename; - if (stripos($argDSN,'UID=') && stripos($argDSN,'PWD=')) $this->_connectionID = db2_pconnect($argDSN,null,null); - else $this->_connectionID = db2_pconnect($argDSN,$argUsername,$argPassword); + + global $php_errormsg; + + $connectionParameters = array('dsn'=>'', + 'uid'=>'', + 'pwd'=>'', + 'database'=>'', + 'catalogue'=>true + ); + + /* + * Uou can either connect to a catalogued connection + * with a database name e.g. 'SAMPLE' + * or an uncatalogued connection with a DSN like connection + * DATABASE=database;HOSTNAME=hostname;PORT=port;PROTOCOL=TCPIP;UID=username;PWD=password; + */ + + if (!$argDSN && !$argDatabasename) + { + $errorMessage = 'Supply either catalogued or uncatalogued connection parameters'; + $this->_errorMsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; + } + + $useCataloguedConnection = true; + $schemaName = ''; + + if ($argDSN && $argDatabasename) + { + /* + * If a catalogued connection if provided, + * as well as user and password + * that will take priority + */ + if ($argUsername && $argPassword && !$this->isDsn($argDatabasename)) + { + if ($this->debug){ + $errorMessage = 'Warning: Because you provided user,'; + $errorMessage.= 'password and database, DSN connection '; + $errorMessage.= 'parameters were discarded'; + ADOConnection::outp($errorMessage); + + } + $argDSN = ''; + } + else if ($this->isDsn($argDSN) && $this->isDsn($argDatabasename)) + { + $errorMessage = 'Supply uncatalogued connection parameters '; + $errorMessage.= 'in either the database or DSN arguments, '; + $errorMessage.= 'but not both'; + $php_errormsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; + } + } + + if (!$this->isDsn($argDSN) && $this->isDsn($argDatabasename)) + { + /* + * Switch them around for next test + */ + $temp = $argDSN; + $argDsn = $argDatabasename; + $argDatabasenME = $temp; } + + if ($this->isDsn($argDSN)) + { + + if (!preg_match('/uid=/i',$argDSN) + || !preg_match('/pwd=/i',$argDSN)) + { + $errorMessage = 'For uncatalogued connections, provide '; + $errorMessage.= 'both UID and PWD in the connection string'; + $php_errormsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; + } + + if (preg_match('/database=/i',$argDSN)) + { + if ($argDatabasename) + { + $argDatabasename = ''; + if ($this->debug) + { + $errorMessage = 'Warning: Because you provided '; + $errorMessage.= 'database information in the DSN '; + $errorMessage.= 'parameters, the supplied database '; + $errorMessage.= 'name was discarded'; + ADOConnection::outp($errorMessage); + } + } + $useCataloguedConnection = false; + + } + elseif ($argDatabasename) + { + $this->databaseName = $argDatabasename; + $argDSN .= ';database=' . $argDatabasename; + $argDatabasename = ''; + $useCataloguedConnection = false; + + } + else + { + $errorMessage = 'Uncatalogued connection parameters '; + $errorMessage.= 'must contain a database= argument'; + $php_errormsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; + } + } + + if ($argDSN && !$argDatabasename && $useCataloguedConnection) + { + $argDatabasename = $argDSN; + $argDSN = ''; + } + - $this->_errorMsg = @db2_conn_errormsg(); - if ($this->_connectionID && $this->autoRollback) @db2_rollback($this->_connectionID); - if (isset($this->connectStmt)) $this->Execute($this->connectStmt); + if ($useCataloguedConnection + && (!$argDatabasename + || !$argUsername + || !$argPassword)) + { + + $errorMessage = 'For catalogued connections, provide '; + $errorMessage.= 'database, username and password'; + $this->_errorMsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; + + } + + if ($argDatabasename) + $this->databaseName = $argDatabasename; + elseif (!$this->databaseName) + $this->databaseName = $this->getDatabasenameFromDsn($argDSN); + + + $connectionParameters = array('dsn'=>$argDSN, + 'uid'=>$argUsername, + 'pwd'=>$argPassword, + 'database'=>$argDatabasename, + 'catalogue'=>$useCataloguedConnection + ); + + return $connectionParameters; + + } - if ($this->_connectionID && isset($schema)) $this->Execute("SET SCHEMA=$schema"); - return $this->_connectionID != false; + /** + * Does the provided string look like a DSN + * + * @param string $dsnString + * + * @return bool + */ + private function isDsn($dsnString){ + $dsnArray = preg_split('/[;=]+/',$dsnString); + if (count($dsnArray) > 2) + return true; + return false; } + - // format and return date string in database timestamp format - function DBTimeStamp($ts, $isfld = false) + /** + * Gets the database name from the DSN + * + * @param string $dsnString + * + * @return string + */ + private function getDatabasenameFromDsn($dsnString){ + + $dsnArray = preg_split('/[;=]+/',$dsnString); + $dbIndex = array_search('database',$dsnArray); + + return $dsnArray[$dbIndex + 1]; + } + + + /** + * format and return date string in database timestamp format + * + * @param mixed $ts either a string or a unixtime + * @param bool $isField discarded + * + * @return string + */ + function dbTimeStamp($ts,$isField=false) { if (empty($ts) && $ts !== 0) return 'null'; - if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts); + if (is_string($ts)) $ts = ADORecordSet::unixTimeStamp($ts); return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')"; } - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) + /** + * Format date column in sql string given an input format that understands Y M D + * + * @param string $fmt + * @param bool $col + * + * @return string + */ + function sqlDate($fmt, $col=false) { - // use right() and replace() ? if (!$col) $col = $this->sysDate; /* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */ @@ -203,10 +481,12 @@ class ADODB_db2 extends ADOConnection { } - function ServerInfo() + function serverInfo() { - $row = $this->GetRow("SELECT service_level, fixpack_num FROM TABLE(sysproc.env_get_inst_info()) - as INSTANCEINFO"); + $sql = "SELECT service_level, fixpack_num + FROM TABLE(sysproc.env_get_inst_info()) + AS INSTANCEINFO"; + $row = $this->GetRow($sql); if ($row) { @@ -214,108 +494,119 @@ class ADODB_db2 extends ADOConnection { $info['fixpack'] = $row[1]; $info['description'] = ''; } else { - return ADOConnection::ServerInfo(); + return ADOConnection::serverInfo(); } return $info; } - function CreateSequence($seqname='adodbseq',$start=1) + function createSequence($seqname='adodbseq',$start=1) { - if (empty($this->_genSeqSQL)) return false; - $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$start)); - if (!$ok) return false; + if (empty($this->_genSeqSQL)) + return false; + + $ok = $this->execute(sprintf($this->_genSeqSQL,$seqname,$start)); + if (!$ok) + return false; return true; } - function DropSequence($seqname = 'adodbseq') + function dropSequence($seqname='adodbseq') { if (empty($this->_dropSeqSQL)) return false; - return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); + return $this->execute(sprintf($this->_dropSeqSQL,$seqname)); } - function SelectLimit($sql, $nrows = -1, $offset = -1, $inputArr = false, $secs2cache = 0) + function selectLimit($sql,$nrows=-1,$offset=-1,$inputArr=false,$secs2cache=0) { - $nrows = (int) $nrows; - $offset = (int) $offset; - if ($offset <= 0) { - // could also use " OPTIMIZE FOR $nrows ROWS " - if ($nrows >= 0) $sql .= " FETCH FIRST $nrows ROWS ONLY "; - $rs = $this->Execute($sql,$inputArr); - } else { + $nrows = (integer) $nrows; + + if ($offset <= 0) + { + if ($nrows >= 0) + $sql .= " FETCH FIRST $nrows ROWS ONLY "; + + $rs = $this->execute($sql,$inputArr); + + } + else + { if ($offset > 0 && $nrows < 0); - else { + + else + { $nrows += $offset; $sql .= " FETCH FIRST $nrows ROWS ONLY "; } - $rs = ADOConnection::SelectLimit($sql,-1,$offset,$inputArr); + + /* + * DB2 has no native support for mid table offset + */ + $rs = ADOConnection::selectLimit($sql,$nrows,$offset,$inputArr); + } - + return $rs; } - /* - This algorithm is not very efficient, but works even if table locking - is not available. - - Will return false if unable to generate an ID after $MAXLOOPS attempts. - */ - function GenID($seq='adodbseq',$start=1) - { - // if you have to modify the parameter below, your database is overloaded, - // or you need to implement generation of id's yourself! - $num = $this->GetOne("VALUES NEXTVAL FOR $seq"); - return $num; - } - - - function ErrorMsg() + + function errorMsg() { - if ($this->_haserrorfunctions) { - if ($this->_errorMsg !== false) return $this->_errorMsg; - if (empty($this->_connectionID)) return @db2_conn_errormsg(); - return @db2_conn_errormsg($this->_connectionID); - } else return ADOConnection::ErrorMsg(); + if ($this->_errorMsg !== false) + return $this->_errorMsg; + + if (empty($this->_connectionID)) + return @db2_conn_errormsg(); + + return @db2_conn_errormsg($this->_connectionID); } - function ErrorNo() + function errorNo() { - if ($this->_haserrorfunctions) { - if ($this->_errorCode !== false) { - // bug in 4.0.6, error number can be corrupted string (should be 6 digits) - return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode; - } - - if (empty($this->_connectionID)) $e = @db2_conn_error(); - else $e = @db2_conn_error($this->_connectionID); + if ($this->_errorCode !== false) + return $this->_errorCode; + - // bug in 4.0.6, error number can be corrupted string (should be 6 digits) - // so we check and patch - if (strlen($e)<=2) return 0; - return $e; - } else return ADOConnection::ErrorNo(); + if (empty($this->_connectionID)) + $e = @db2_conn_error(); + + else + $e = @db2_conn_error($this->_connectionID); + + return $e; } - function BeginTrans() + function beginTrans() { - if (!$this->hasTransactions) return false; - if ($this->transOff) return true; + if (!$this->hasTransactions) + return false; + if ($this->transOff) + return true; + $this->transCnt += 1; + $this->_autocommit = false; + return db2_autocommit($this->_connectionID,false); } function CommitTrans($ok=true) { - if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); - if ($this->transCnt) $this->transCnt -= 1; + if ($this->transOff) + return true; + + if (!$ok) + return $this->RollbackTrans(); + + if ($this->transCnt) + $this->transCnt -= 1; + $this->_autocommit = true; - $ret = db2_commit($this->_connectionID); - db2_autocommit($this->_connectionID,true); + $ret = @db2_commit($this->_connectionID); + @db2_autocommit($this->_connectionID,true); return $ret; } @@ -324,123 +615,400 @@ class ADODB_db2 extends ADOConnection { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->_autocommit = true; - $ret = db2_rollback($this->_connectionID); - db2_autocommit($this->_connectionID,true); + $ret = @db2_rollback($this->_connectionID); + @db2_autocommit($this->_connectionID,true); return $ret; } - function MetaPrimaryKeys($table, $owner = false) + /** + * Return a list of Primary Keys for a specified table + * + * We don't use db2_statistics as the function does not seem to play + * well with mixed case table names + * + * @param string $table + * @param bool $primary (optional) only return primary keys + * @param bool $owner (optional) not used in this driver + * + * @return string[] Array of indexes + */ + public function metaPrimaryKeys($table,$owner=false) { - global $ADODB_FETCH_MODE; + + $primaryKeys = array(); + + global $ADODB_FETCH_MODE; - if ($this->uCaseTables) $table = strtoupper($table); $schema = ''; $this->_findschema($table,$schema); + + $table = $this->getTableCasedValue($table); - $savem = $ADODB_FETCH_MODE; + $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $qid = @db2_primarykeys($this->_connectionID,'',$schema,$table); + $this->setFetchMode(ADODB_FETCH_NUM); - if (!$qid) { - $ADODB_FETCH_MODE = $savem; - return false; - } - $rs = new ADORecordSet_db2($qid); - $ADODB_FETCH_MODE = $savem; - if (!$rs) return false; + $sql = "SELECT * + FROM syscat.indexes + WHERE tabname='$table'"; + + $rows = $this->getAll($sql); + + $this->setFetchMode($savem); + $ADODB_FETCH_MODE = $savem; - $arr = $rs->GetArray(); - $rs->Close(); - $arr2 = array(); - for ($i=0; $i < sizeof($arr); $i++) { - if ($arr[$i][3]) $arr2[] = $arr[$i][3]; + if (empty($rows)) + return false; + + foreach ($rows as $r) + { + if ($r[7] != 'P') + continue; + + $cols = explode('+',$r[6]); + foreach ($cols as $colIndex=>$col) + { + if ($colIndex == 0) + continue; + $columnName = $this->getMetaCasedValue($col); + $primaryKeys[] = $columnName; + } + break; } - return $arr2; + return $primaryKeys; } - function MetaForeignKeys($table, $owner = FALSE, $upper = FALSE, $asociative = FALSE ) + /** + * returns assoc array where keys are tables, and values are foreign keys + * + * @param string $table + * @param string $owner [optional][discarded] + * @param bool $upper [optional][discarded] + * @param bool $associative[optional][discarded] + * + * @return mixed[] Array of foreign key information + */ + public function metaForeignKeys($table, $owner = FALSE, $upper = FALSE, $asociative = FALSE ) { - global $ADODB_FETCH_MODE; + + global $ADODB_FETCH_MODE; - if ($this->uCaseTables) $table = strtoupper($table); $schema = ''; $this->_findschema($table,$schema); $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $qid = @db2_foreign_keys($this->_connectionID,'',$schema,$table); - if (!$qid) { - $ADODB_FETCH_MODE = $savem; + + $this->setFetchMode(ADODB_FETCH_NUM); + + $sql = "SELECT SUBSTR(tabname,1,20) table_name, + SUBSTR(constname,1,20) fk_name, + SUBSTR(REFTABNAME,1,12) parent_table, + SUBSTR(refkeyname,1,20) pk_orig_table, + fk_colnames + FROM syscat.references + WHERE tabname = '$table'"; + + $results = $this->getAll($sql); + + $ADODB_FETCH_MODE = $savem; + $this->setFetchMode($savem); + + if (empty($results)) return false; + + $foreignKeys = array(); + + foreach ($results as $r) + { + $parentTable = trim($this->getMetaCasedValue($r[2])); + $keyName = trim($this->getMetaCasedValue($r[1])); + $foreignKeys[$parentTable] = $keyName; } - $rs = new ADORecordSet_db2($qid); + + return $foreignKeys; + } - $ADODB_FETCH_MODE = $savem; + /** + * Returns a list of tables + * + * @param string $ttype (optional) + * @param string $schema (optional) + * @param string $mask (optional) + * + * @return array + */ + public function metaTables($ttype=false,$schema=false,$mask=false) + { + + global $ADODB_FETCH_MODE; + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + /* - $rs->fields indices - 0 PKTABLE_CAT - 1 PKTABLE_SCHEM - 2 PKTABLE_NAME - 3 PKCOLUMN_NAME - 4 FKTABLE_CAT - 5 FKTABLE_SCHEM - 6 FKTABLE_NAME - 7 FKCOLUMN_NAME + * Values for TABLE_TYPE + * --------------------------- + * ALIAS, HIERARCHY TABLE, INOPERATIVE VIEW, NICKNAME, + * MATERIALIZED QUERY TABLE, SYSTEM TABLE, TABLE, + * TYPED TABLE, TYPED VIEW, and VIEW + * + * If $ttype passed as '', match 'TABLE' and 'VIEW' + * If $ttype passed as 'T' it is assumed to be 'TABLE' + * if $ttype passed as 'V' it is assumed to be 'VIEW' */ - if (!$rs) return false; - - $foreign_keys = array(); - while (!$rs->EOF) { - if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { - if (!is_array($foreign_keys[$rs->fields[5].'.'.$rs->fields[6]])) - $foreign_keys[$rs->fields[5].'.'.$rs->fields[6]] = array(); - $foreign_keys[$rs->fields[5].'.'.$rs->fields[6]][$rs->fields[7]] = $rs->fields[3]; - } - $rs->MoveNext(); + $ttype = strtoupper($ttype); + if ($ttype) { + /* + * @todo We could do valid type checking or array type + */ + if ($ttype == 'V') + $ttype = 'VIEW'; + if ($ttype == 'T') + $ttype = 'TABLE'; } + + if (!$schema) + $schema = '%'; + + if (!$mask) + $mask = '%'; + + $qid = @db2_tables($this->_connectionID,NULL,$schema,$mask,$ttype); + $rs = new ADORecordSet_db2($qid); + + $ADODB_FETCH_MODE = $savem; + + if (!$rs) + return false; + + $arr = $rs->getArray(); + $rs->Close(); - return $foreign_key; - } + + $tableList = array(); + /* + * Array items + * --------------------------------- + * 0 TABLE_CAT The catalog that contains the table. + * The value is NULL if this table does not have catalogs. + * 1 TABLE_SCHEM Name of the schema that contains the table. + * 2 TABLE_NAME Name of the table. + * 3 TABLE_TYPE Table type identifier for the table. + * 4 REMARKS Description of the table. + */ + + for ($i=0; $i < sizeof($arr); $i++) + { + + $tableRow = $arr[$i]; + $tableName = $tableRow[2]; + $tableType = $tableRow[3]; + + if (!$tableName) + continue; + + if ($ttype == '' && (strcmp($tableType,'TABLE') <> 0 && strcmp($tableType,'VIEW') <> 0)) + continue; + + /* + * Set metacasing if required + */ + $tableName = $this->getMetaCasedValue($tableName); + + /* + * If we requested a schema, we prepend the schema + name to the table name + */ + if (strcmp($schema,'%') <> 0) + $tableName = $schema . '.' . $tableName; + + $tableList[] = $tableName; + + } + return $tableList; + } + + /** + * Return a list of indexes for a specified table + * + * We don't use db2_statistics as the function does not seem to play + * well with mixed case table names + * + * @param string $table + * @param bool $primary (optional) only return primary keys + * @param bool $owner (optional) not used in this driver + * + * @return string[] Array of indexes + */ + public function metaIndexes($table, $primary = false, $owner = false) { + + global $ADODB_FETCH_MODE; - function MetaTables($ttype = false, $schema = false, $mask = false) - { - global $ADODB_FETCH_MODE; + /* Array( + * [name_of_index] => Array( + * [unique] => true or false + * [columns] => Array( + * [0] => firstcol + * [1] => nextcol + * [2] => etc........ + * ) + * ) + * ) + */ + $indices = array(); + $primaryKeyName = ''; + + $table = $this->getTableCasedValue($table); - $savem = $ADODB_FETCH_MODE; + + $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $qid = db2_tables($this->_connectionID); - - $rs = new ADORecordSet_db2($qid); + $this->setFetchMode(ADODB_FETCH_NUM); + $sql = "SELECT * + FROM syscat.indexes + WHERE tabname='$table'"; + + $rows = $this->getAll($sql); + + $this->setFetchMode($savem); $ADODB_FETCH_MODE = $savem; - if (!$rs) { - $false = false; - return $false; - } - - $arr = $rs->GetArray(); - $rs->Close(); - $arr2 = array(); + + if (empty($rows)) + return false; - if ($ttype) { - $isview = strncmp($ttype,'V',1) === 0; + foreach ($rows as $r) + { + + $primaryIndex = $r[7] == 'P'?1:0; + if (!$primary) + /* + * Primary key not requested, ignore that one + */ + if ($r[7] == 'P') + continue; + + $indexName = $this->getMetaCasedValue($r[1]); + if (!isset($indices[$indexName])) + { + $unique = ($r[7] == 'U')?1:0; + $indices[$indexName] = array('unique'=>$unique, + 'primary'=>$primaryIndex, + 'columns'=>array() + ); + } + $cols = explode('+',$r[6]); + foreach ($cols as $colIndex=>$col) + { + if ($colIndex == 0) + continue; + $columnName = $this->getMetaCasedValue($col); + $indices[$indexName]['columns'][] = $columnName; + } + } - for ($i=0; $i < sizeof($arr); $i++) { - if (!$arr[$i][2]) continue; - $type = $arr[$i][3]; - $owner = $arr[$i][1]; - $schemaval = ($schema) ? $arr[$i][1].'.' : ''; - if ($ttype) { - if ($isview) { - if (strncmp($type,'V',1) === 0) $arr2[] = $schemaval.$arr[$i][2]; - } else if (strncmp($owner,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2]; - } else if (strncmp($owner,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2]; + + return $indices; + + } + + /** + * List procedures or functions in an array. + * + * We interrogate syscat.routines instead of calling the PHP + * function procedures because ADOdb requires the type of procedure + * this is not available in the php function + * + * @param string $procedureNamePattern (optional) + * @param string $catalog (optional) + * @param string $schemaPattern (optional) + + * @return array of procedures on current database. + * + */ + public function metaProcedures($procedureNamePattern = null, $catalog = null, $schemaPattern = null) { + + + global $ADODB_FETCH_MODE; + + $metaProcedures = array(); + $procedureSQL = ''; + $catalogSQL = ''; + $schemaSQL = ''; + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($procedureNamePattern) + $procedureSQL = "AND ROUTINENAME LIKE " . strtoupper($this->qstr($procedureNamePattern)); + + if ($catalog) + $catalogSQL = "AND OWNER=" . strtoupper($this->qstr($catalog)); + + if ($schemaPattern) + $schemaSQL = "AND ROUTINESCHEMA LIKE {$this->qstr($schemaPattern)}"; + + + $fields = " + ROUTINENAME, + CASE ROUTINETYPE + WHEN 'P' THEN 'PROCEDURE' + WHEN 'F' THEN 'FUNCTION' + ELSE 'METHOD' + END AS ROUTINETYPE_NAME, + ROUTINESCHEMA, + REMARKS"; + + $SQL = "SELECT $fields + FROM syscat.routines + WHERE OWNER IS NOT NULL + $procedureSQL + $catalogSQL + $schemaSQL + ORDER BY ROUTINENAME + "; + + $result = $this->execute($SQL); + + $ADODB_FETCH_MODE = $savem; + + if (!$result) + return false; + + while ($r = $result->fetchRow()){ + $procedureName = $this->getMetaCasedValue($r[0]); + $schemaName = $this->getMetaCasedValue($r[2]); + $metaProcedures[$procedureName] = array('type'=> $r[1], + 'catalog' => '', + 'schema' => $schemaName, + 'remarks' => $r[3] + ); } - return $arr2; + + return $metaProcedures; + } + + /** + * Lists databases. Because instances are independent, we only know about + * the current database name + * + * @return string[] + */ + public function metaDatabases(){ + + $dbName = $this->getMetaCasedValue($this->databaseName); + + return (array)$dbName; + + } + + + /* See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2datetime_data_type_changes.asp @@ -510,26 +1078,32 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 } } - function MetaColumns($table, $normalize=true) + public function metaColumns($table, $normalize=true) { - global $ADODB_FETCH_MODE; - - $false = false; - if ($this->uCaseTables) $table = strtoupper($table); - $schema = ''; - $this->_findschema($table,$schema); - + global $ADODB_FETCH_MODE; + $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $colname = "%"; - $qid = db2_columns($this->_connectionID, "", $schema, $table, $colname); - if (empty($qid)) return $false; + $schema = '%'; + $this->_findschema($table,$schema); + $table = $this->getTableCasedValue($table); + $colname = "%"; + $qid = db2_columns($this->_connectionID, null, $schema, $table, $colname); + if (empty($qid)) + { + if ($this->debug) + { + $errorMessage = @db2_conn_errormsg($this->_connectionID); + ADOConnection::outp($errorMessage); + } + return false; + } $rs = new ADORecordSet_db2($qid); - $ADODB_FETCH_MODE = $savem; - if (!$rs) return $false; + if (!$rs) + return false; + $rs->_fetch(); $retarr = array(); @@ -548,15 +1122,25 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 9 RADIX 10 NULLABLE 11 REMARKS + 12 Column Default + 13 SQL Data Type + 14 SQL DateTime SubType + 15 Max length in Octets + 16 Ordinal Position + 17 Is NULLABLE */ - while (!$rs->EOF) { - if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { - $fld = new ADOFieldObject(); + while (!$rs->EOF) + { + if ($rs->fields[2] == $table) + { + + $fld = new ADOFieldObject(); $fld->name = $rs->fields[3]; $fld->type = $this->DB2Types($rs->fields[4]); // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp // access uses precision to store length for char/varchar + if ($fld->type == 'C' or $fld->type == 'X') { if ($rs->fields[4] <= -95) // UNICODE $fld->max_length = $rs->fields[7]/2; @@ -564,24 +1148,42 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 $fld->max_length = $rs->fields[7]; } else $fld->max_length = $rs->fields[7]; - $fld->not_null = !empty($rs->fields[10]); - $fld->scale = $rs->fields[8]; - $fld->primary_key = false; - $retarr[strtoupper($fld->name)] = $fld; - } else if (sizeof($retarr)>0) + + $fld->not_null = !empty($rs->fields[10]); + $fld->scale = $rs->fields[8]; + $fld->primary_key = false; + + //$columnName = $this->getMetaCasedValue($fld->name); + $columnName = strtoupper($fld->name); + $retarr[$columnName] = $fld; + + } + else if (sizeof($retarr)>0) break; + $rs->MoveNext(); + } + $rs->Close(); - if (empty($retarr)) $retarr = false; + if (empty($retarr)) + $retarr = false; - $qid = db2_primary_keys($this->_connectionID, "", $schema, $table); - if (empty($qid)) return $false; + /* + * Now we find out if the column is part of a primary key + */ + + $qid = @db2_primary_keys($this->_connectionID, "", $schema, $table); + if (empty($qid)) + return false; $rs = new ADORecordSet_db2($qid); - $ADODB_FETCH_MODE = $savem; - if (!$rs) return $retarr; + if (!$rs) + { + $ADODB_FETCH_MODE = $savem; + return $retarr; + } $rs->_fetch(); /* @@ -594,23 +1196,371 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 5 PK_NAME */ while (!$rs->EOF) { - if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { + if (strtoupper(trim($rs->fields[2])) == $table + && (!$schema || strtoupper($rs->fields[1]) == $schema)) + { $retarr[strtoupper($rs->fields[3])]->primary_key = true; - } else if (sizeof($retarr)>0) + } + else if (sizeof($retarr)>0) break; + $rs->MoveNext(); } $rs->Close(); - if (empty($retarr)) $retarr = false; + $ADODB_FETCH_MODE = $savem; + + if (empty($retarr)) + return false; + + /* + * If the fetch mode is numeric, return as numeric array + */ + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) + $retarr = array_values($retarr); + return $retarr; } + /** + * In this version if prepareSp, we just check to make sure + * that the name of the stored procedure is correct + * If true, we returns an array + * else false + * + * @param string $procedureName + * @param mixed $parameters (not used in db2 connections) + * @return mixed[] + */ + function prepareSp($procedureName,$parameters=false) { + + global $ADODB_FETCH_MODE; + + $this->storedProcedureParameters = array('name'=>'', + 'resource'=>false, + 'in'=>array(), + 'out'=>array(), + 'index'=>array(), + 'parameters'=>array(), + 'keyvalue' => array()); + + //$procedureName = strtoupper($procedureName); + //$procedureName = $this->getTableCasedValue($procedureName); + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $qid = db2_procedures($this->_connectionID, NULL , '%' , $procedureName ); + + $ADODB_FETCH_MODE = $savem; + + if (!$qid) + { + if ($this->debug) + ADOConnection::outp(sprintf('No Procedure of name %s available',$procedureName)); + return false; + } + + + + $this->storedProcedureParameters['name'] = $procedureName; + /* + * Now we know we have a valid procedure name, lets see if it requires + * parameters + */ + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $qid = db2_procedure_columns($this->_connectionID, NULL , '%' , $procedureName , NULL ); + + $ADODB_FETCH_MODE = $savem; + + if (!$qid) + { + if ($this->debug) + ADOConnection::outp(sprintf('No columns of name %s available',$procedureName)); + return false; + } + $rs = new ADORecordSet_db2($qid); + if (!$rs) + return false; + + $preparedStatement = 'CALL %s(%s)'; + $parameterMarkers = array(); + while (!$rs->EOF) + { + $parameterName = $rs->fields[3]; + if ($parameterName == '') + { + $rs->moveNext(); + continue; + } + $parameterType = $rs->fields[4]; + $ordinalPosition = $rs->fields[17]; + switch($parameterType) + { + case DB2_PARAM_IN: + case DB2_PARAM_INOUT: + $this->storedProcedureParameters['in'][$parameterName] = ''; + break; + case DB2_PARAM_INOUT: + case DB2_PARAM_OUT: + $this->storedProcedureParameters['out'][$parameterName] = ''; + break; + } + $this->storedProcedureParameters['index'][$parameterName] = $ordinalPosition; + $this->storedProcedureParameters['parameters'][$ordinalPosition] = $rs->fields; + $rs->moveNext(); + + } + $parameterCount = count($this->storedProcedureParameters['index']); + $parameterMarkers = array_fill(0,$parameterCount,'?'); + + /* + * We now know how many parameters to bind to the stored procedure + */ + $parameterList = implode(',',$parameterMarkers); + + $sql = sprintf($preparedStatement,$procedureName,$parameterList); + + $spResource = @db2_prepare($this->_connectionID,$sql); + + if (!$spResource) + { + $errorMessage = @db2_conn_errormsg($this->_connectionID); + $this->_errorMsg = $errorMessage; + + if ($this->debug) + ADOConnection::outp($errorMessage); + + return false; + } + + $this->storedProcedureParameters['resource'] = $spResource; + + if ($this->debug) + { + + ADOConnection::outp('The following parameters will be used in the SP call'); + ADOConnection::outp(print_r($this->storedProcedureParameters)); + } + /* + * We now have a stored parameter resource + * to bind to. The spResource and sql that is returned are + * not usable, its for dummy compatibility. Everything + * will be handled by the storedProcedureParameters + * array + */ + return array($sql,$spResource); + + } + + private function storedProcedureParameter(&$stmt, + &$var, + $name, + $isOutput=false, + $maxLen=4000, + $type=false) + { + + + $name = strtoupper($name); + + /* + * Must exist in the list of parameter names for the type + */ + if ($isOutput + && !isset( $this->storedProcedureParameters['out'][$name])) + { + $errorMessage = sprintf('%s is not a valid OUT parameter name',$name); + + $this->_errorMsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return false; + } + + if (!$isOutput + && !isset( $this->storedProcedureParameters['in'][$name])) + { + $errorMessage = sprintf('%s is not a valid IN parameter name',$name); + + $this->_errorMsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return false; + } + + /* + * We will use these values to bind to when we execute + * the query + */ + $this->storedProcedureParameters['keyvalue'][$name] = &$var; + + return true; + + } + + /** + * Executes a prepared stored procedure. + * + * The function uses the previously accumulated information and + * resources in the $storedProcedureParameters array + * + * @return mixed The statement id if successful, or false + */ + private function executeStoredProcedure() + { + + /* + * Get the previously built resource + */ + $stmtid = $this->storedProcedureParameters['resource']; + + /* + * Bind our variables to the DB2 procedure + */ + foreach ($this->storedProcedureParameters['keyvalue'] as $spName=>$spValue){ + + /* + * Get the ordinal position, required for binding + */ + $ordinalPosition = $this->storedProcedureParameters['index'][$spName]; + + /* + * Get the db2 column dictionary for the parameter + */ + $columnDictionary = $this->storedProcedureParameters['parameters'][$ordinalPosition]; + $parameterType = $columnDictionary[4]; + $dataType = $columnDictionary[5]; + $precision = $columnDictionary[10]; + $scale = $columnDictionary[9]; + + $ok = @db2_bind_param ($this->storedProcedureParameters['resource'], + $ordinalPosition , + $spName, + $parameterType, + $dataType, + $precision, + $scale + ); + + if (!$ok) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); - function Prepare($sql) + if ($this->debug) + ADOConnection::outp($this->_errorMsg); + return false; + } + + if ($this->debug) + ADOConnection::outp("Correctly Bound parameter $spName to procedure"); + + /* + * Build a variable in the current environment that matches + * the parameter name + */ + ${$spName} = $spValue; + + } + + /* + * All bound, execute + */ + + if (!@db2_execute($stmtid)) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + + if ($this->debug) + ADOConnection::outp($this->_errorMsg); + return false; + } + + /* + * We now take the changed parameters back into the + * stored procedures array where we can query them later + * Remember that $spValue was passed in by reference, so we + * can access the value in the variable that was originally + * passed to inParameter or outParameter + */ + foreach ($this->storedProcedureParameters['keyvalue'] as $spName=>$spValue) + { + /* + * We make it available to the environment + */ + $spValue = ${$spName}; + $this->storedProcedureParameters['keyvalue'][$spName] = $spValue; + } + + return $stmtid; + } + + /** + * + * Accepts an input or output parameter to bind to either a stored + * or prepared statements. For DB2, this should not be called as an + * API. always wrap with inParameter and outParameter + * + * @param mixed[] $stmt Statement returned by Prepare() or PrepareSP(). + * @param mixed $var PHP variable to bind to. Can set to null (for isNull support). + * @param string $name Name of stored procedure variable name to bind to. + * @param int $isOutput optional) Indicates direction of parameter + * 0/false=IN 1=OUT 2= IN/OUT + * This is ignored for Stored Procedures + * @param int $maxLen (optional)Holds an maximum length of the variable. + * This is ignored for Stored Procedures + * @param int $type (optional) The data type of $var. + * This is ignored for Stored Procedures + * + * @return bool Success of the operation + */ + public function parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false) + { + + /* + * If the $stmt is the name of a stored procedure we are + * setting up, we will process it one way, otherwise + * we assume we are setting up a prepared statement + */ + if (is_array($stmt)) + { + if ($this->debug) + ADOConnection::outp("Adding parameter to stored procedure"); + if ($stmt[1] == $this->storedProcedureParameters['resource']) + return $this->storedProcedureParameter($stmt[1], + $var, + $name, + $isOutput, + $maxLen, + $type); + + } + + /* + * We are going to add a parameter to a prepared statement + */ + if ($this->debug) + ADOConnection::outp("Adding parameter to prepared statement"); + } + + + /** + * Prepares a prepared SQL statement, not used for stored procedures + * + * @param string $sql + * + * @return mixed + */ + function prepare($sql) { + if (! $this->_bindInputArray) return $sql; // no binding - $stmt = db2_prepare($this->_connectionID,$sql); + + $stmt = @db2_prepare($this->_connectionID,$sql); if (!$stmt) { // we don't know whether db2 driver is parsing prepared stmts, so just return sql return $sql; @@ -618,66 +1568,158 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 return array($sql,$stmt,false); } - /* returns queryID or false */ - function _query($sql,$inputarr=false) + /** + * Executes a query + * + * @param mixed $sql + * @param mixed $inputarr An optional array of parameters + * + * @return mixed either the queryID or false + */ + function _query(&$sql,$inputarr=false) { - $last_php_error = $this->resetLastError(); - $this->_errorMsg = ''; + + GLOBAL $php_errormsg; + + if (isset($php_errormsg)) + $php_errormsg = ''; + $this->_error = ''; - if ($inputarr) { - if (is_array($sql)) { + $db2Options = array(); + /* + * Use DB2 Internal case handling for best speed + */ + switch(ADODB_ASSOC_CASE) + { + case ADODB_ASSOC_CASE_UPPER: + $db2Options = array('db2_attr_case'=>DB2_CASE_UPPER); + $setOption = @db2_set_option($this->_connectionID,$db2Options,1); + break; + + case ADODB_ASSOC_CASE_LOWER: + $db2Options = array('db2_attr_case'=>DB2_CASE_LOWER); + $setOption = @db2_set_option($this->_connectionID,$db2Options,1); + break; + + default: + $db2Options = array('db2_attr_case'=>DB2_CASE_NATURAL); + $setOption = @db2_set_option($this->_connectionID,$db2Options,1); + } + + $db2Options = array('db2_attr_case'=>DB2_CASE_LOWER); + $setOption = @db2_set_option($this->_connectionID,$db2Options,1); + + if ($inputarr) + { + if (is_array($sql)) + { $stmtid = $sql[1]; - } else { - $stmtid = db2_prepare($this->_connectionID,$sql); + } + else + { + $stmtid = @db2_prepare($this->_connectionID,$sql); - if ($stmtid == false) { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); + if ($stmtid == false) + { + $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; return false; } } - if (! db2_execute($stmtid,$inputarr)) { - if ($this->_haserrorfunctions) { - $this->_errorMsg = db2_stmt_errormsg(); - $this->_errorCode = db2_stmt_error(); - } + if (! @db2_execute($stmtid,$inputarr)) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + if ($this->debug) + ADOConnection::outp($this->_errorMsg); return false; } - } else if (is_array($sql)) { - $stmtid = $sql[1]; - if (!db2_execute($stmtid)) { - if ($this->_haserrorfunctions) { - $this->_errorMsg = db2_stmt_errormsg(); - $this->_errorCode = db2_stmt_error(); + } + else if (is_array($sql)) + { + + /* + * Either a prepared statement or a stored procedure + */ + + if (is_array($this->storedProcedureParameters) + && is_resource($this->storedProcedureParameters['resource'] + )) + /* + * This is all handled in the separate method for + * readability + */ + return $this->executeStoredProcedure(); + + /* + * First, we prepare the statement + */ + $stmtid = @db2_prepare($this->_connectionID,$sql[0]); + if (!$stmtid){ + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + if ($this->debug) + ADOConnection::outp("Prepare failed: " . $this->_errorMsg); + + return false; + } + /* + * We next bind some input parameters + */ + $ordinal = 1; + foreach ($sql[1] as $psVar=>$psVal){ + ${$psVar} = $psVal; + $ok = @db2_bind_param($stmtid, $ordinal, $psVar, DB2_PARAM_IN); + if (!$ok) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + if ($this->debug) + ADOConnection::outp("Bind failed: " . $this->_errorMsg); + return false; } + } + + if (!@db2_execute($stmtid)) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + if ($this->debug) + ADOConnection::outp($this->_errorMsg); return false; } - } else - $stmtid = @db2_exec($this->_connectionID,$sql); + + return $stmtid; + } + else + { + $stmtid = @db2_exec($this->_connectionID,$sql); + } $this->_lastAffectedRows = 0; - if ($stmtid) { - if (@db2_num_fields($stmtid) == 0) { + if ($stmtid) + { + if (@db2_num_fields($stmtid) == 0) + { $this->_lastAffectedRows = db2_num_rows($stmtid); $stmtid = true; - } else { + } + else + { $this->_lastAffectedRows = 0; } - if ($this->_haserrorfunctions) { - $this->_errorMsg = ''; - $this->_errorCode = 0; - } else { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - } - } else { - if ($this->_haserrorfunctions) { - $this->_errorMsg = db2_stmt_errormsg(); - $this->_errorCode = db2_stmt_error(); - } else { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - } + $this->_errorMsg = ''; + $this->_errorCode = 0; + + } + else + { + + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + } return $stmtid; } @@ -688,12 +1730,12 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 Usage: - $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); */ - function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + function updateBlob($table,$column,$val,$where,$blobtype='BLOB') { - return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; + return $this->execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; } // returns true or false @@ -708,6 +1750,87 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 { return $this->_lastAffectedRows; } + + /** + * Gets a meta cased parameter + * + * Receives an input variable to be processed per the metaCasing + * rule, and returns the same value, processed + * + * @param string $value + * + * @return string + */ + final public function getMetaCasedValue($value) + { + global $ADODB_ASSOC_CASE; + + switch($ADODB_ASSOC_CASE) + { + case ADODB_ASSOC_CASE_LOWER: + $value = strtolower($value); + break; + case ADODB_ASSOC_CASE_UPPER: + $value = strtoupper($value); + break; + } + return $value; + } + + + const TABLECASE_LOWER = 0; + const TABLECASE_UPPER = 1; + const TABLECASE_DEFAULT = 2; + + /* + * Controls the casing of the table provided to the meta functions + */ + private $tableCase = 2; + + /** + * Sets the table case parameter + * + * @param int $caseOption + * @return null + */ + final public function setTableCasing($caseOption) + { + $this->tableCase = $caseOption; + } + + /** + * Gets the table casing parameter + * + * @return int $caseOption + */ + final public function getTableCasing() + { + return $this->tableCase; + } + + /** + * Gets a table cased parameter + * + * Receives an input variable to be processed per the tableCasing + * rule, and returns the same value, processed + * + * @param string $value + * + * @return string + */ + final public function getTableCasedValue($value) + { + switch($this->tableCase) + { + case self::TABLECASE_LOWER: + $value = strtolower($value); + break; + case self::TABLECASE_UPPER: + $value = strtoupper($value); + break; + } + return $value; + } } @@ -735,21 +1858,30 @@ class ADORecordSet_db2 extends ADORecordSet { // returns the field object - function FetchField($offset = -1) + function fetchField($offset = 0) { - $o= new ADOFieldObject(); - $o->name = @db2_field_name($this->_queryID,$offset); - $o->type = @db2_field_type($this->_queryID,$offset); - $o->max_length = db2_field_width($this->_queryID,$offset); - if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); - else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); + $o = new ADOFieldObject(); + $o->name = @db2_field_name($this->_queryID,$offset); + $o->type = @db2_field_type($this->_queryID,$offset); + $o->max_length = @db2_field_width($this->_queryID,$offset); + + /* + if (ADODB_ASSOC_CASE == 0) + $o->name = strtolower($o->name); + else if (ADODB_ASSOC_CASE == 1) + $o->name = strtoupper($o->name); + */ return $o; } /* Use associative array to get fields array */ - function Fields($colname) + function fields($colname) { - if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; + + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + return $this->fields[$colname]; + } + if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { @@ -764,11 +1896,15 @@ class ADORecordSet_db2 extends ADORecordSet { function _initrs() { - global $ADODB_COUNTRECS; + global $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1; + $this->_numOfFields = @db2_num_fields($this->_queryID); + // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 - if ($this->_numOfRows == 0) $this->_numOfRows = -1; + + if ($this->_numOfRows == 0) + $this->_numOfRows = -1; } function _seek($row) @@ -776,21 +1912,15 @@ class ADORecordSet_db2 extends ADORecordSet { return false; } - // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated - function GetArrayLimit($nrows,$offset=-1) + function getArrayLimit($nrows,$offset=0) { if ($offset <= 0) { $rs = $this->GetArray($nrows); return $rs; } - $savem = $this->fetchMode; - $this->fetchMode = ADODB_FETCH_NUM; + $this->Move($offset); - $this->fetchMode = $savem; - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(); - } $results = array(); $cnt = 0; @@ -798,46 +1928,81 @@ class ADORecordSet_db2 extends ADORecordSet { $results[$cnt++] = $this->fields; $this->MoveNext(); } - + return $results; } - - function MoveNext() + function moveNext() { - if ($this->_numOfRows != 0 && !$this->EOF) { - $this->_currentRow++; - + if ($this->EOF || $this->_numOfRows == 0) + return false; + + $this->_currentRow++; + + $this->processCoreFetch(); + return $this->processMoveRecord(); + + } + + final private function processCoreFetch() + { + switch ($this->fetchMode){ + case ADODB_FETCH_ASSOC: + + /* + * Associative array + */ + $this->fields = @db2_fetch_assoc($this->_queryID); + break; + + case ADODB_FETCH_BOTH: + /* + * Fetch both numeric and Associative array + */ + $this->fields = @db2_fetch_both($this->_queryID); + break; + default: + /* + * Numeric array + */ $this->fields = @db2_fetch_array($this->_queryID); - if ($this->fields) { - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(); - } - return true; - } + break; } - $this->fields = false; - $this->EOF = true; - return false; - } + } - function _fetch() + final private function processMoveRecord() + { + if (!$this->fields){ + $this->EOF = true; + return false; + } + + return true; + } + + function _fetch() { - - $this->fields = db2_fetch_array($this->_queryID); - if ($this->fields) { - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(); - } + $this->processCoreFetch(); + if ($this->fields) return true; - } + $this->fields = false; return false; } function _close() { - return @db2_free_result($this->_queryID); + $ok = @db2_free_result($this->_queryID); + if (!$ok) + { + $this->_errorMsg = @db2_stmt_errormsg($this->_queryId); + $this->_errorCode = @db2_stmt_error(); + + if ($this->debug) + ADOConnection::outp($this->_errorMsg); + return false; + } + } } diff --git a/drivers/adodb-db2oci.inc.php b/drivers/adodb-db2oci.inc.php index c026dfdb..2bf26d98 100644 --- a/drivers/adodb-db2oci.inc.php +++ b/drivers/adodb-db2oci.inc.php @@ -15,49 +15,19 @@ Set tabs to 4 for best viewing. // security - hide paths if (!defined('ADODB_DIR')) die(); -include(ADODB_DIR."/drivers/adodb-db2.inc.php"); +include_once(ADODB_DIR."/drivers/adodb-db2.inc.php"); if (!defined('ADODB_DB2OCI')){ define('ADODB_DB2OCI',1); -/* -// regex code for smart remapping of :0, :1 bind vars to ? ? -function _colontrack($p) -{ -global $_COLONARR,$_COLONSZ; - $v = (integer) substr($p,1); - if ($v > $_COLONSZ) return $p; - $_COLONARR[] = $v; - return '?'; -} - -// smart remapping of :0, :1 bind vars to ? ? -function _colonscope($sql,$arr) -{ -global $_COLONARR,$_COLONSZ; - - $_COLONARR = array(); - $_COLONSZ = sizeof($arr); - - $sql2 = preg_replace("/(:[0-9]+)/e","_colontrack('\\1')",$sql); - - if (empty($_COLONARR)) return array($sql,$arr); - - foreach($_COLONARR as $k => $v) { - $arr2[] = $arr[$v]; - } - - return array($sql2,$arr2); -} -*/ - -/* - Smart remapping of :0, :1 bind vars to ? ? - - Handles colons in comments -- and / * * / and in quoted strings. -*/ - +/** + * Smart remapping of :0, :1 bind vars to ? ? + * Handles colons in comments -- and / * * / and in quoted strings. + * @param string $sql SQL statement + * @param array $arr parameters + * @return array + */ function _colonparser($sql,$arr) { $lensql = strlen($sql); @@ -217,10 +187,6 @@ class ADORecordSet_db2oci extends ADORecordSet_db2 { var $databaseType = "db2oci"; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } } //define diff --git a/drivers/adodb-db2ora.inc.php b/drivers/adodb-db2ora.inc.php index d883bfd2..e0dcec7a 100644 --- a/drivers/adodb-db2ora.inc.php +++ b/drivers/adodb-db2ora.inc.php @@ -15,7 +15,7 @@ Set tabs to 4 for best viewing. // security - hide paths if (!defined('ADODB_DIR')) die(); -include(ADODB_DIR."/drivers/adodb-db2.inc.php"); +include_once(ADODB_DIR."/drivers/adodb-db2.inc.php"); if (!defined('ADODB_DB2OCI')){ @@ -77,10 +77,6 @@ class ADORecordSet_db2oci extends ADORecordSet_odbc { var $databaseType = "db2oci"; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } } //define diff --git a/drivers/adodb-fbsql.inc.php b/drivers/adodb-fbsql.inc.php index 4b108536..9acf9243 100644 --- a/drivers/adodb-fbsql.inc.php +++ b/drivers/adodb-fbsql.inc.php @@ -25,10 +25,6 @@ class ADODB_fbsql extends ADOConnection { var $fmtTimeStamp = "'Y-m-d H:i:s'"; var $hasLimit = false; - function __construct() - { - } - function _insertid() { return fbsql_insert_id($this->_connectionID); @@ -177,7 +173,7 @@ class ADORecordSet_fbsql extends ADORecordSet{ default: $this->fetchMode = FBSQL_BOTH; break; } - return parent::__construct($queryID); + parent::__construct($queryID); } function _initrs() @@ -259,7 +255,7 @@ class ADORecordSet_fbsql extends ADORecordSet{ if (!empty($fieldobj->primary_key)) return 'R'; else return 'I'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } diff --git a/drivers/adodb-firebird.inc.php b/drivers/adodb-firebird.inc.php index 050fe33f..58d71dbf 100644 --- a/drivers/adodb-firebird.inc.php +++ b/drivers/adodb-firebird.inc.php @@ -10,18 +10,114 @@ Set tabs to 4 for best viewing. Latest version is available at http://adodb.org/ + firebird data driver. Requires firebird client. Works on Windows and Unix. + */ // security - hide paths if (!defined('ADODB_DIR')) die(); -include_once(ADODB_DIR."/drivers/adodb-ibase.inc.php"); - -class ADODB_firebird extends ADODB_ibase { +class ADODB_firebird extends ADOConnection { var $databaseType = "firebird"; + var $dataProvider = "firebird"; + var $replaceQuote = "''"; // string to use to replace quotes + var $fbird_datefmt = '%Y-%m-%d'; // For hours,mins,secs change to '%Y-%m-%d %H:%M:%S'; + var $fmtDate = "'Y-m-d'"; + var $fbird_timestampfmt = "%Y-%m-%d %H:%M:%S"; + var $fbird_timefmt = "%H:%M:%S"; + var $fmtTimeStamp = "'Y-m-d, H:i:s'"; + var $concat_operator='||'; + var $_transactionID; + var $metaTablesSQL = "select lower(rdb\$relation_name) from rdb\$relations where rdb\$relation_name not like 'RDB\$%'"; + //OPN STUFF start + var $metaColumnsSQL = "select lower(a.rdb\$field_name), a.rdb\$null_flag, a.rdb\$default_source, b.rdb\$field_length, b.rdb\$field_scale, b.rdb\$field_sub_type, b.rdb\$field_precision, b.rdb\$field_type from rdb\$relation_fields a, rdb\$fields b where a.rdb\$field_source = b.rdb\$field_name and a.rdb\$relation_name = '%s' order by a.rdb\$field_position asc"; + //OPN STUFF end + var $ibasetrans; + var $hasGenID = true; + var $_bindInputArray = true; + var $buffers = 0; var $dialect = 3; - + var $sysDate = "cast('TODAY' as timestamp)"; var $sysTimeStamp = "CURRENT_TIMESTAMP"; //"cast('NOW' as timestamp)"; + var $ansiOuter = true; + var $hasAffectedRows = true; + var $poorAffectedRows = false; + var $blobEncodeType = 'C'; + var $role = false; + var $nameQuote = ''; /// string to use to quote identifiers and names + + function __construct() + { + // Ignore IBASE_DEFAULT we want a more practical transaction! + // if (defined('IBASE_DEFAULT')) $this->ibasetrans = IBASE_DEFAULT; + // else + $this->ibasetrans = IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED; + } + + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$persist=false) + { + if (!function_exists('fbird_pconnect')) return null; + if ($argDatabasename) $argHostname .= ':'.$argDatabasename; + $fn = ($persist) ? 'fbird_pconnect':'fbird_connect'; + if ($this->role) + $this->_connectionID = $fn($argHostname,$argUsername,$argPassword, + $this->charSet,$this->buffers,$this->dialect,$this->role); + else + $this->_connectionID = $fn($argHostname,$argUsername,$argPassword, + $this->charSet,$this->buffers,$this->dialect); + + if ($this->dialect != 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html + $this->replaceQuote = "''"; + } + if ($this->_connectionID === false) { + $this->_handleerror(); + return false; + } + + // PHP5 change. + if (function_exists('fbird_timefmt')) { + fbird_timefmt($this->fbird_datefmt,fbird_DATE ); + if ($this->dialect == 1) { + fbird_timefmt($this->fbird_datefmt,fbird_TIMESTAMP ); + } else { + fbird_timefmt($this->fbird_timestampfmt,fbird_TIMESTAMP ); + } + fbird_timefmt($this->fbird_timefmt,fbird_TIME ); + + } else { + ini_set("ibase.timestampformat", $this->fbird_timestampfmt); + ini_set("ibase.dateformat", $this->fbird_datefmt); + ini_set("ibase.timeformat", $this->fbird_timefmt); + } + return true; + } + + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,true); + } + + + function MetaPrimaryKeys($table,$owner_notused=false,$internalKey=false) + { + if ($internalKey) { + return array('RDB$DB_KEY'); + } + + $table = strtoupper($table); + + $sql = 'SELECT S.RDB$FIELD_NAME AFIELDNAME + FROM RDB$INDICES I JOIN RDB$INDEX_SEGMENTS S ON I.RDB$INDEX_NAME=S.RDB$INDEX_NAME + WHERE I.RDB$RELATION_NAME=\''.$table.'\' and I.RDB$INDEX_NAME like \'RDB$PRIMARY%\' + ORDER BY I.RDB$INDEX_NAME,S.RDB$FIELD_POSITION'; + + $a = $this->GetCol($sql,false,true); + if ($a && sizeof($a)>0) return $a; + return false; + } function ServerInfo() { @@ -38,10 +134,597 @@ class ADODB_firebird extends ADODB_ibase { return $arr; } + function BeginTrans() + { + if ($this->transOff) return true; + $this->transCnt += 1; + $this->autoCommit = false; + $this->_transactionID = fbird_trans( $this->ibasetrans, $this->_connectionID ); + return $this->_transactionID; + } + + function CommitTrans($ok=true) + { + if (!$ok) { + return $this->RollbackTrans(); + } + if ($this->transOff) { + return true; + } + if ($this->transCnt) { + $this->transCnt -= 1; + } + $ret = false; + $this->autoCommit = true; + if ($this->_transactionID) { + //print ' commit '; + $ret = fbird_commit($this->_transactionID); + } + $this->_transactionID = false; + return $ret; + } + + function _affectedrows() + { + return fbird_affected_rows( $this->_transactionID ? $this->_transactionID : $this->_connectionID ); + } + + // there are some compat problems with ADODB_COUNTRECS=false and $this->_logsql currently. + // it appears that ibase extension cannot support multiple concurrent queryid's + function _Execute($sql,$inputarr=false) { + global $ADODB_COUNTRECS; + + if ($this->_logsql) { + $savecrecs = $ADODB_COUNTRECS; + $ADODB_COUNTRECS = true; // force countrecs + $ret =& ADOConnection::_Execute($sql,$inputarr); + $ADODB_COUNTRECS = $savecrecs; + } else { + $ret = ADOConnection::_Execute($sql,$inputarr); + } + return $ret; + } + + function RollbackTrans() + { + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $ret = false; + $this->autoCommit = true; + if ($this->_transactionID) { + $ret = fbird_rollback($this->_transactionID); + } + $this->_transactionID = false; + + return $ret; + } + + function &MetaIndexes ($table, $primary = FALSE, $owner=false) + { + // save old fetch mode + global $ADODB_FETCH_MODE; + $false = false; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } + $table = strtoupper($table); + $sql = "SELECT * FROM RDB\$INDICES WHERE RDB\$RELATION_NAME = '".$table."'"; + if (!$primary) { + $sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$%'"; + } else { + $sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$FOREIGN%'"; + } + // get index details + $rs = $this->Execute($sql); + if (!is_object($rs)) { + // restore fetchmode + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + return $false; + } + + $indexes = array(); + while ($row = $rs->FetchRow()) { + $index = $row[0]; + if (!isset($indexes[$index])) { + if (is_null($row[3])) { + $row[3] = 0; + } + $indexes[$index] = array( + 'unique' => ($row[3] == 1), + 'columns' => array() + ); + } + $sql = "SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '".$index."' ORDER BY RDB\$FIELD_POSITION ASC"; + $rs1 = $this->Execute($sql); + while ($row1 = $rs1->FetchRow()) { + $indexes[$index]['columns'][$row1[2]] = $row1[1]; + } + } + // restore fetchmode + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + + return $indexes; + } + + + // See http://community.borland.com/article/0,1410,25844,00.html + function RowLock($tables,$where,$col=false) + { + if ($this->autoCommit) { + $this->BeginTrans(); + } + $this->Execute("UPDATE $table SET $col=$col WHERE $where "); // is this correct - jlim? + return 1; + } + + + function CreateSequence($seqname = 'adodbseq', $startID = 1) + { + $ok = $this->Execute(("CREATE GENERATOR $seqname" )); + if (!$ok) return false; + return $this->Execute("SET GENERATOR $seqname TO ".($startID-1)); + } + + function DropSequence($seqname = 'adodbseq') + { + $seqname = strtoupper($seqname); + return $this->Execute("DROP GENERATOR $seqname"); + } + + function GenID($seqname='adodbseq',$startID=1) + { + $getnext = ("SELECT Gen_ID($seqname,1) FROM RDB\$DATABASE"); + $rs = @$this->Execute($getnext); + if (!$rs) { + $this->Execute(("CREATE GENERATOR $seqname" )); + $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';'); + $rs = $this->Execute($getnext); + } + if ($rs && !$rs->EOF) { + $this->genID = (integer) reset($rs->fields); + } + else { + $this->genID = 0; // false + } + + if ($rs) { + $rs->Close(); + } + + return $this->genID; + } + + function SelectDB($dbName) + { + return false; + } + + function _handleerror() + { + $this->_errorMsg = fbird_errmsg(); + } + + function ErrorNo() + { + if (preg_match('/error code = ([\-0-9]*)/i', $this->_errorMsg,$arr)) return (integer) $arr[1]; + else return 0; + } + + function ErrorMsg() + { + return $this->_errorMsg; + } + + function Prepare($sql) + { + $stmt = fbird_prepare($this->_connectionID,$sql); + if (!$stmt) return false; + return array($sql,$stmt); + } + + // returns query ID if successful, otherwise false + // there have been reports of problems with nested queries - the code is probably not re-entrant? + function _query($sql,$iarr=false) + { + if ( !$this->isConnected() ) return false; + if (!$this->autoCommit && $this->_transactionID) { + $conn = $this->_transactionID; + $docommit = false; + } else { + $conn = $this->_connectionID; + $docommit = true; + } + if (is_array($sql)) { + $fn = 'fbird_execute'; + $sql = $sql[1]; + if (is_array($iarr)) { + if ( !isset($iarr[0]) ) + $iarr[0] = ''; // PHP5 compat hack + $fnarr = array_merge( array($sql) , $iarr); + $ret = call_user_func_array($fn,$fnarr); + } + else { + $ret = $fn($sql); + } + } else { + $fn = 'fbird_query'; + if (is_array($iarr)) + { + if (sizeof($iarr) == 0) + $iarr[0] = ''; // PHP5 compat hack + $fnarr = array_merge( array($conn,$sql) , $iarr); + $ret = call_user_func_array($fn,$fnarr); + } + else { + $ret = $fn($conn, $sql); + } + } + if ($docommit && $ret === true) { + fbird_commit($this->_connectionID); + } + + $this->_handleerror(); + return $ret; + } + + // returns true or false + function _close() + { + if (!$this->autoCommit) { + @fbird_rollback($this->_connectionID); + } + return @fbird_close($this->_connectionID); + } + + //OPN STUFF start + function _ConvertFieldType(&$fld, $ftype, $flen, $fscale, $fsubtype, $fprecision, $dialect3) + { + $fscale = abs($fscale); + $fld->max_length = $flen; + $fld->scale = null; + switch($ftype){ + case 7: + case 8: + if ($dialect3) { + switch($fsubtype){ + case 0: + $fld->type = ($ftype == 7 ? 'smallint' : 'integer'); + break; + case 1: + $fld->type = 'numeric'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + case 2: + $fld->type = 'decimal'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + } // switch + } else { + if ($fscale !=0) { + $fld->type = 'decimal'; + $fld->scale = $fscale; + $fld->max_length = ($ftype == 7 ? 4 : 9); + } else { + $fld->type = ($ftype == 7 ? 'smallint' : 'integer'); + } + } + break; + case 16: + if ($dialect3) { + switch($fsubtype){ + case 0: + $fld->type = 'decimal'; + $fld->max_length = 18; + $fld->scale = 0; + break; + case 1: + $fld->type = 'numeric'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + case 2: + $fld->type = 'decimal'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + } // switch + } + break; + case 10: + $fld->type = 'float'; + break; + case 14: + $fld->type = 'char'; + break; + case 27: + if ($fscale !=0) { + $fld->type = 'decimal'; + $fld->max_length = 15; + $fld->scale = 5; + } else { + $fld->type = 'double'; + } + break; + case 35: + if ($dialect3) { + $fld->type = 'timestamp'; + } else { + $fld->type = 'date'; + } + break; + case 12: + $fld->type = 'date'; + break; + case 13: + $fld->type = 'time'; + break; + case 37: + $fld->type = 'varchar'; + break; + case 40: + $fld->type = 'cstring'; + break; + case 261: + $fld->type = 'blob'; + $fld->max_length = -1; + break; + } // switch + } + //OPN STUFF end + + // returns array of ADOFieldObjects for current table + function MetaColumns($table, $normalize=true) + { + global $ADODB_FETCH_MODE; + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); + + $ADODB_FETCH_MODE = $save; + $false = false; + if ($rs === false) { + return $false; + } + + $retarr = array(); + //OPN STUFF start + $dialect3 = ($this->dialect==3 ? true : false); + //OPN STUFF end + while (!$rs->EOF) { //print_r($rs->fields); + $fld = new ADOFieldObject(); + $fld->name = trim($rs->fields[0]); + //OPN STUFF start + $this->_ConvertFieldType($fld, $rs->fields[7], $rs->fields[3], $rs->fields[4], $rs->fields[5], $rs->fields[6], $dialect3); + if (isset($rs->fields[1]) && $rs->fields[1]) { + $fld->not_null = true; + } + if (isset($rs->fields[2])) { + + $fld->has_default = true; + $d = substr($rs->fields[2],strlen('default ')); + switch ($fld->type) + { + case 'smallint': + case 'integer': $fld->default_value = (int) $d; break; + case 'char': + case 'blob': + case 'text': + case 'varchar': $fld->default_value = (string) substr($d,1,strlen($d)-2); break; + case 'double': + case 'float': $fld->default_value = (float) $d; break; + default: $fld->default_value = $d; break; + } + // case 35:$tt = 'TIMESTAMP'; break; + } + if ((isset($rs->fields[5])) && ($fld->type == 'blob')) { + $fld->sub_type = $rs->fields[5]; + } else { + $fld->sub_type = null; + } + //OPN STUFF end + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; + else $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + if ( empty($retarr)) return $false; + else return $retarr; + } + + function BlobEncode( $blob ) + { + $blobid = fbird_blob_create( $this->_connectionID); + fbird_blob_add( $blobid, $blob ); + return fbird_blob_close( $blobid ); + } + + // since we auto-decode all blob's since 2.42, + // BlobDecode should not do any transforms + function BlobDecode($blob) + { + return $blob; + } + + // old blobdecode function + // still used to auto-decode all blob's + function _BlobDecode_old( $blob ) + { + $blobid = fbird_blob_open($this->_connectionID, $blob ); + $realblob = fbird_blob_get( $blobid,$this->maxblobsize); // 2nd param is max size of blob -- Kevin Boillet <kevinboillet@yahoo.fr> + while($string = fbird_blob_get($blobid, 8192)){ + $realblob .= $string; + } + fbird_blob_close( $blobid ); + + return( $realblob ); + } + + function _BlobDecode( $blob ) + { + $blob_data = fbird_blob_info($this->_connectionID, $blob ); + $blobid = fbird_blob_open($this->_connectionID, $blob ); + + if( $blob_data[0] > $this->maxblobsize ) { + $realblob = fbird_blob_get($blobid, $this->maxblobsize); + + while($string = fbird_blob_get($blobid, 8192)) { + $realblob .= $string; + } + } else { + $realblob = fbird_blob_get($blobid, $blob_data[0]); + } + + fbird_blob_close( $blobid ); + return( $realblob ); + } + + function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') + { + $fd = fopen($path,'rb'); + if ($fd === false) return false; + $blob_id = fbird_blob_create($this->_connectionID); + + /* fill with data */ + + while ($val = fread($fd,32768)){ + fbird_blob_add($blob_id, $val); + } + + /* close and get $blob_id_str for inserting into table */ + $blob_id_str = fbird_blob_close($blob_id); + + fclose($fd); + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; + } + + /* + Insert a null into the blob field of the table first. + Then use UpdateBlob to store the blob. + + Usage: + + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + $blob_id = fbird_blob_create($this->_connectionID); + + // fbird_blob_add($blob_id, $val); + + // replacement that solves the problem by which only the first modulus 64K / + // of $val are stored at the blob field //////////////////////////////////// + // Thx Abel Berenstein aberenstein#afip.gov.ar + $len = strlen($val); + $chunk_size = 32768; + $tail_size = $len % $chunk_size; + $n_chunks = ($len - $tail_size) / $chunk_size; + + for ($n = 0; $n < $n_chunks; $n++) { + $start = $n * $chunk_size; + $data = substr($val, $start, $chunk_size); + fbird_blob_add($blob_id, $data); + } + + if ($tail_size) { + $start = $n_chunks * $chunk_size; + $data = substr($val, $start, $tail_size); + fbird_blob_add($blob_id, $data); + } + // end replacement ///////////////////////////////////////////////////////// + + $blob_id_str = fbird_blob_close($blob_id); + + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; + + } + + + function OldUpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + $blob_id = fbird_blob_create($this->_connectionID); + fbird_blob_add($blob_id, $val); + $blob_id_str = fbird_blob_close($blob_id); + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; + } + + // Format date column in sql string given an input format that understands Y M D + // Only since Interbase 6.0 - uses EXTRACT + // problem - does not zero-fill the day and month yet + function SQLDate($fmt, $col=false) + { + if (!$col) $col = $this->sysDate; + $s = ''; + + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + if ($s) $s .= '||'; + $ch = $fmt[$i]; + switch($ch) { + case 'Y': + case 'y': + $s .= "extract(year from $col)"; + break; + case 'M': + case 'm': + $s .= "extract(month from $col)"; + break; + case 'W': + case 'w': + // The more accurate way of doing this is with a stored procedure + // See http://wiki.firebirdsql.org/wiki/index.php?page=DATE+Handling+Functions for details + $s .= "((extract(yearday from $col) - extract(weekday from $col - 1) + 7) / 7)"; + break; + case 'Q': + case 'q': + $s .= "cast(((extract(month from $col)+2) / 3) as integer)"; + break; + case 'D': + case 'd': + $s .= "(extract(day from $col))"; + break; + case 'H': + case 'h': + $s .= "(extract(hour from $col))"; + break; + case 'I': + case 'i': + $s .= "(extract(minute from $col))"; + break; + case 'S': + case 's': + $s .= "CAST((extract(second from $col)) AS INTEGER)"; + break; + + default: + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + $s .= $this->qstr($ch); + break; + } + } + return $s; + } + // Note that Interbase 6.5 uses this ROWS instead - don't you love forking wars! // SELECT col1, col2 FROM table ROWS 5 -- get 5 rows // SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2 - function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $secs=0) + function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $secs=0) { $nrows = (integer) $nrows; $offset = (integer) $offset; @@ -58,16 +741,173 @@ class ADODB_firebird extends ADODB_ibase { return $rs; } +} -}; - +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ -class ADORecordSet_firebird extends ADORecordSet_ibase { +class ADORecordset_firebird extends ADORecordSet +{ var $databaseType = "firebird"; + var $bind=false; + var $_cacheType; function __construct($id,$mode=false) { - parent::__construct($id,$mode); + global $ADODB_FETCH_MODE; + + $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; + parent::__construct($id); + } + + /** + * Get column information in the Recordset object. + * fetchField() can be used in order to obtain information about fields in + * a certain query result. If the field offset isn't specified, the next + * field that wasn't yet retrieved by fetchField() is retrieved. + * @return object containing field information. + */ + function FetchField($fieldOffset = -1) + { + $fld = new ADOFieldObject; + $ibf = fbird_field_info($this->_queryID,$fieldOffset); + + $name = empty($ibf['alias']) ? $ibf['name'] : $ibf['alias']; + + switch (ADODB_ASSOC_CASE) { + case ADODB_ASSOC_CASE_UPPER: + $fld->name = strtoupper($name); + break; + case ADODB_ASSOC_CASE_LOWER: + $fld->name = strtolower($name); + break; + case ADODB_ASSOC_CASE_NATIVE: + default: + $fld->name = $name; + break; + } + + $fld->type = $ibf['type']; + $fld->max_length = $ibf['length']; + + /* This needs to be populated from the metadata */ + $fld->not_null = false; + $fld->has_default = false; + $fld->default_value = 'null'; + return $fld; + } + + function _initrs() + { + $this->_numOfRows = -1; + $this->_numOfFields = @fbird_num_fields($this->_queryID); + + // cache types for blob decode check + for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { + $f1 = $this->FetchField($i); + $this->_cacheType[] = $f1->type; + } + } + + function _seek($row) + { + return false; + } + + function _fetch() + { + $f = @fbird_fetch_row($this->_queryID); + if ($f === false) { + $this->fields = false; + return false; + } + // OPN stuff start - optimized + // fix missing nulls and decode blobs automatically + + global $ADODB_ANSI_PADDING_OFF; + //$ADODB_ANSI_PADDING_OFF=1; + $rtrim = !empty($ADODB_ANSI_PADDING_OFF); + + for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { + if ($this->_cacheType[$i]=="BLOB") { + if (isset($f[$i])) { + $f[$i] = $this->connection->_BlobDecode($f[$i]); + } else { + $f[$i] = null; + } + } else { + if (!isset($f[$i])) { + $f[$i] = null; + } else if ($rtrim && is_string($f[$i])) { + $f[$i] = rtrim($f[$i]); + } + } + } + // OPN stuff end + + $this->fields = $f; + if ($this->fetchMode == ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(); + } else if ($this->fetchMode == ADODB_FETCH_BOTH) { + $this->fields = array_merge($this->fields,$this->GetRowAssoc()); + } + return true; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; + if (!$this->bind) { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } + + return $this->fields[$this->bind[strtoupper($colname)]]; + } + + + function _close() + { + return @fbird_free_result($this->_queryID); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + switch (strtoupper($t)) { + case 'CHAR': + return 'C'; + + case 'TEXT': + case 'VARCHAR': + case 'VARYING': + if ($len <= $this->blobSize) return 'C'; + return 'X'; + case 'BLOB': + return 'B'; + + case 'TIMESTAMP': + case 'DATE': return 'D'; + case 'TIME': return 'T'; + //case 'T': return 'T'; + + //case 'L': return 'L'; + case 'INT': + case 'SHORT': + case 'INTEGER': return 'I'; + default: return ADODB_DEFAULT_METATYPE; + } + } + } diff --git a/drivers/adodb-ibase.inc.php b/drivers/adodb-ibase.inc.php index 634a4524..c83265b0 100644 --- a/drivers/adodb-ibase.inc.php +++ b/drivers/adodb-ibase.inc.php @@ -350,46 +350,26 @@ class ADODB_ibase extends ADOConnection { $fn = 'ibase_execute'; $sql = $sql[1]; if (is_array($iarr)) { - if (ADODB_PHPVER >= 0x4050) { // actually 4.0.4 - if ( !isset($iarr[0]) ) $iarr[0] = ''; // PHP5 compat hack - $fnarr = array_merge( array($sql) , $iarr); - $ret = call_user_func_array($fn,$fnarr); - } else { - switch(sizeof($iarr)) { - case 1: $ret = $fn($sql,$iarr[0]); break; - case 2: $ret = $fn($sql,$iarr[0],$iarr[1]); break; - case 3: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2]); break; - case 4: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; - case 5: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; - case 6: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; - case 7: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; - default: ADOConnection::outp( "Too many parameters to ibase query $sql"); - case 8: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; - } - } - } else $ret = $fn($sql); + if ( !isset($iarr[0]) ) + $iarr[0] = ''; // PHP5 compat hack + $fnarr = array_merge( array($sql) , $iarr); + $ret = call_user_func_array($fn,$fnarr); + } + else { + $ret = $fn($sql); + } } else { $fn = 'ibase_query'; if (is_array($iarr)) { - if (ADODB_PHPVER >= 0x4050) { // actually 4.0.4 - if (sizeof($iarr) == 0) $iarr[0] = ''; // PHP5 compat hack - $fnarr = array_merge( array($conn,$sql) , $iarr); - $ret = call_user_func_array($fn,$fnarr); - } else { - switch(sizeof($iarr)) { - case 1: $ret = $fn($conn,$sql,$iarr[0]); break; - case 2: $ret = $fn($conn,$sql,$iarr[0],$iarr[1]); break; - case 3: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2]); break; - case 4: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; - case 5: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; - case 6: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; - case 7: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; - default: ADOConnection::outp( "Too many parameters to ibase query $sql"); - case 8: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; - } - } - } else $ret = $fn($conn,$sql); + if (sizeof($iarr) == 0) + $iarr[0] = ''; // PHP5 compat hack + $fnarr = array_merge( array($conn,$sql) , $iarr); + $ret = call_user_func_array($fn,$fnarr); + } + else { + $ret = $fn($conn, $sql); + } } if ($docommit && $ret === true) { ibase_commit($this->_connectionID); @@ -601,14 +581,8 @@ class ADODB_ibase extends ADOConnection { function _BlobDecode( $blob ) { - if (ADODB_PHPVER >= 0x5000) { - $blob_data = ibase_blob_info($this->_connectionID, $blob ); - $blobid = ibase_blob_open($this->_connectionID, $blob ); - } else { - - $blob_data = ibase_blob_info( $blob ); - $blobid = ibase_blob_open( $blob ); - } + $blob_data = ibase_blob_info($this->_connectionID, $blob ); + $blobid = ibase_blob_open($this->_connectionID, $blob ); if( $blob_data[0] > $this->maxblobsize ) { @@ -911,7 +885,7 @@ class ADORecordset_ibase extends ADORecordSet case 'INT': case 'SHORT': case 'INTEGER': return 'I'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } diff --git a/drivers/adodb-informix.inc.php b/drivers/adodb-informix.inc.php index 559444a5..45ab8c6f 100644 --- a/drivers/adodb-informix.inc.php +++ b/drivers/adodb-informix.inc.php @@ -33,9 +33,4 @@ class ADODB_informix extends ADODB_informix72 { class ADORecordset_informix extends ADORecordset_informix72 { var $databaseType = "informix"; - - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-informix72.inc.php b/drivers/adodb-informix72.inc.php index 2ff41ca6..123fc0e5 100644 --- a/drivers/adodb-informix72.inc.php +++ b/drivers/adodb-informix72.inc.php @@ -403,7 +403,7 @@ class ADORecordset_informix72 extends ADORecordSet { $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; - return parent::__construct($id); + parent::__construct($id); } @@ -501,7 +501,7 @@ class ADORecordset_informix72 extends ADORecordSet { } /** !Eos -* Auxiliar function to Parse coltype,collength. Used by Metacolumns +* Auxiliary function to Parse coltype,collength. Used by Metacolumns * return: array ($mtype,$length,$precision,$nullable) (similar to ifx_fieldpropierties) */ function ifx_props($coltype,$collength){ diff --git a/drivers/adodb-ldap.inc.php b/drivers/adodb-ldap.inc.php index 34b31e0a..a54bb09a 100644 --- a/drivers/adodb-ldap.inc.php +++ b/drivers/adodb-ldap.inc.php @@ -45,10 +45,6 @@ class ADODB_ldap extends ADOConnection { # error on binding, eg. "Binding: invalid credentials" var $_bind_errmsg = "Binding: %s"; - function __construct() - { - } - // returns true or false function _connect( $host, $username, $password, $ldapbase) @@ -331,7 +327,7 @@ class ADORecordSet_ldap extends ADORecordSet{ /* Return whole recordset as a multi-dimensional associative array */ - function GetAssoc($force_array = false, $first2cols = false) + function GetAssoc($force_array = false, $first2cols = false, $fetchMode = -1) { $records = $this->_numOfRows; $results = array(); diff --git a/drivers/adodb-mssql.inc.php b/drivers/adodb-mssql.inc.php index ceef7521..5d8a05b1 100644 --- a/drivers/adodb-mssql.inc.php +++ b/drivers/adodb-mssql.inc.php @@ -39,39 +39,7 @@ if (!defined('ADODB_DIR')) die(); //---------------------------------------------------------------- -// has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc -if (ADODB_PHPVER >= 0x4300) { -// docs say 4.2.0, but testing shows only since 4.3.0 does it work! - ini_set('mssql.datetimeconvert',0); -} else { -global $ADODB_mssql_mths; // array, months must be upper-case - - - $ADODB_mssql_date_order = 'mdy'; - $ADODB_mssql_mths = array( - 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6, - 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); -} - -//--------------------------------------------------------------------------- -// Call this to autoset $ADODB_mssql_date_order at the beginning of your code, -// just after you connect to the database. Supports mdy and dmy only. -// Not required for PHP 4.2.0 and above. -function AutoDetect_MSSQL_Date_Order($conn) -{ -global $ADODB_mssql_date_order; - $adate = $conn->GetOne('select getdate()'); - if ($adate) { - $anum = (int) $adate; - if ($anum > 0) { - if ($anum > 31) { - //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently"); - } else - $ADODB_mssql_date_order = 'dmy'; - } else - $ADODB_mssql_date_order = 'mdy'; - } -} +ini_set('mssql.datetimeconvert',0); class ADODB_mssql extends ADOConnection { var $databaseType = "mssql"; @@ -94,7 +62,6 @@ class ADODB_mssql extends ADOConnection { var $hasGenID = true; var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)'; var $sysTimeStamp = 'GetDate()'; - var $_has_mssql_init; var $maxParameterLen = 4000; var $arrayClass = 'ADORecordSet_array_mssql'; var $uniqueSort = true; @@ -107,11 +74,6 @@ class ADODB_mssql extends ADOConnection { var $_bindInputArray = true; var $forceNewConnect = false; - function __construct() - { - $this->_has_mssql_init = (strnatcmp(PHP_VERSION,'4.1.0')>=0); - } - function ServerInfo() { global $ADODB_FETCH_MODE; @@ -155,9 +117,9 @@ class ADODB_mssql extends ADOConnection { // the same scope. A scope is a module -- a stored procedure, trigger, // function, or batch. Thus, two statements are in the same scope if // they are in the same stored procedure, function, or batch. - if ($this->lastInsID !== false) { - return $this->lastInsID; // InsID from sp_executesql call - } else { + if ($this->lastInsID !== false) { + return $this->lastInsID; // InsID from sp_executesql call + } else { return $this->GetOne($this->identitySQL); } } @@ -177,22 +139,22 @@ class ADODB_mssql extends ADOConnection { */ function qstr($s,$magic_quotes=false) { - if (!$magic_quotes) { - return "'".str_replace("'",$this->replaceQuote,$s)."'"; + if (!$magic_quotes) { + return "'".str_replace("'",$this->replaceQuote,$s)."'"; } - // undo magic quotes for " unless sybase is on - $sybase = ini_get('magic_quotes_sybase'); - if (!$sybase) { - $s = str_replace('\\"','"',$s); - if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything - return "'$s'"; - else {// change \' to '' for sybase/mssql - $s = str_replace('\\\\','\\',$s); - return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; - } - } else { - return "'".$s."'"; + // undo magic quotes for " unless sybase is on + $sybase = ini_get('magic_quotes_sybase'); + if (!$sybase) { + $s = str_replace('\\"','"',$s); + if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything + return "'$s'"; + else {// change \' to '' for sybase/mssql + $s = str_replace('\\\\','\\',$s); + return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; + } + } else { + return "'".$s."'"; } } // moodle change end - see readme_moodle.txt @@ -309,7 +271,9 @@ class ADODB_mssql extends ADOConnection { case 'A': $s .= "substring(convert(char(19),$col,0),18,2)"; break; - + case 'l': + $s .= "datename(dw,$col)"; + break; default: if ($ch == '\\') { $i++; @@ -327,8 +291,8 @@ class ADODB_mssql extends ADOConnection { { if ($this->transOff) return true; $this->transCnt += 1; - $ok = $this->Execute('BEGIN TRAN'); - return $ok; + $ok = $this->Execute('BEGIN TRAN'); + return $ok; } function CommitTrans($ok=true) @@ -451,29 +415,29 @@ class ADODB_mssql extends ADOConnection { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - if ($this->fetchMode !== FALSE) { - $savem = $this->SetFetchMode(FALSE); - } + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } - $rs = $this->Execute($sql); - if (isset($savem)) { - $this->SetFetchMode($savem); - } - $ADODB_FETCH_MODE = $save; + $rs = $this->Execute($sql); + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; - if (!is_object($rs)) { - return FALSE; - } + if (!is_object($rs)) { + return FALSE; + } $indexes = array(); while ($row = $rs->FetchRow()) { if ($primary && !$row[5]) continue; - $indexes[$row[0]]['unique'] = $row[6]; - $indexes[$row[0]]['columns'][] = $row[1]; - } - return $indexes; + $indexes[$row[0]]['unique'] = $row[6]; + $indexes[$row[0]]['columns'][] = $row[1]; + } + return $indexes; } function MetaForeignKeys($table, $owner=false, $upper=false) @@ -488,7 +452,7 @@ class ADODB_mssql extends ADOConnection { "select object_name(constid) as constraint_name, col_name(fkeyid, fkey) as column_name, object_name(rkeyid) as referenced_table_name, - col_name(rkeyid, rkey) as referenced_column_name + col_name(rkeyid, rkey) as referenced_column_name from sysforeignkeys where upper(object_name(fkeyid)) = $table order by constraint_name, referenced_table_name, keyno"; @@ -509,7 +473,11 @@ order by constraint_name, referenced_table_name, keyno"; foreach($arr as $k => $v) { foreach($v as $a => $b) { if ($upper) $a = strtoupper($a); - $arr2[$a] = $b; + if (is_array($arr2[$a])) { // a previous foreign key was define for this reference table, we merge the new one + $arr2[$a] = array_merge($arr2[$a], $b); + } else { + $arr2[$a] = $b; + } } } return $arr2; @@ -519,22 +487,24 @@ order by constraint_name, referenced_table_name, keyno"; function MetaDatabases() { if(@mssql_select_db("master")) { - $qry=$this->metaDatabasesSQL; - if($rs=@mssql_query($qry,$this->_connectionID)){ - $tmpAr=$ar=array(); - while($tmpAr=@mssql_fetch_row($rs)) - $ar[]=$tmpAr[0]; - @mssql_select_db($this->database); - if(sizeof($ar)) - return($ar); - else - return(false); - } else { - @mssql_select_db($this->database); - return(false); - } - } - return(false); + $qry = $this->metaDatabasesSQL; + if($rs = @mssql_query($qry,$this->_connectionID)) { + $tmpAr = $ar = array(); + while($tmpAr = @mssql_fetch_row($rs)) { + $ar[]=$tmpAr[0]; + } + @mssql_select_db($this->database); + if(sizeof($ar)) { + return($ar); + } else { + return(false); + } + } else { + @mssql_select_db($this->database); + return(false); + } + } + return(false); } // "Stein-Aksel Basma" <basma@accelero.no> @@ -607,14 +577,18 @@ order by constraint_name, referenced_table_name, keyno"; if (!$id) return false; $arr = mssql_fetch_array($id); @mssql_free_result($id); - if (is_array($arr)) return $arr[0]; - else return -1; + if (is_array($arr)) { + return $arr[0]; + } else { + return -1; + } } // returns true or false, newconnect supported since php 5.1.0. function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$newconnect=false) { if (!function_exists('mssql_pconnect')) return null; + if (!empty($this->port)) $argHostname .= ":".$this->port; $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword,$newconnect); if ($this->_connectionID === false) return false; if ($argDatabasename) return $this->SelectDB($argDatabasename); @@ -626,6 +600,7 @@ order by constraint_name, referenced_table_name, keyno"; function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('mssql_pconnect')) return null; + if (!empty($this->port)) $argHostname .= ":".$this->port; $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; @@ -639,9 +614,9 @@ order by constraint_name, referenced_table_name, keyno"; } function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { + { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true); - } + } function Prepare($sql) { @@ -656,31 +631,27 @@ order by constraint_name, referenced_table_name, keyno"; function PrepareSP($sql,$param=true) { - if (!$this->_has_mssql_init) { - ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); - return $sql; - } $stmt = mssql_init($sql,$this->_connectionID); if (!$stmt) return $sql; return array($sql,$stmt); } // returns concatenated string - // MSSQL requires integers to be cast as strings - // automatically cast every datatype to VARCHAR(255) - // @author David Rogers (introspectshun) - function Concat() - { - $s = ""; - $arr = func_get_args(); + // MSSQL requires integers to be cast as strings + // automatically cast every datatype to VARCHAR(255) + // @author David Rogers (introspectshun) + function Concat() + { + $s = ""; + $arr = func_get_args(); - // Split single record on commas, if possible - if (sizeof($arr) == 1) { - foreach ($arr as $arg) { - $args = explode(',', $arg); - } - $arr = $args; - } + // Split single record on commas, if possible + if (sizeof($arr) == 1) { + foreach ($arr as $arg) { + $args = explode(',', $arg); + } + $arr = $args; + } array_walk( $arr, @@ -688,11 +659,11 @@ order by constraint_name, referenced_table_name, keyno"; $value = "CAST(" . $value . " AS VARCHAR(255))"; } ); - $s = implode('+',$arr); - if (sizeof($arr) > 0) return "$s"; + $s = implode('+',$arr); + if (sizeof($arr) > 0) return "$s"; return ''; - } + } /* Usage: @@ -714,11 +685,6 @@ order by constraint_name, referenced_table_name, keyno"; */ function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false) { - if (!$this->_has_mssql_init) { - ADOConnection::outp( "Parameter: mssql_bind only available since PHP 4.1.0"); - return false; - } - $isNull = is_null($var); // php 4.0.4 and above... if ($type === false) @@ -728,7 +694,7 @@ order by constraint_name, referenced_table_name, keyno"; case 'double': $type = SQLFLT8; break; case 'integer': $type = SQLINT4; break; case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0 - } + } if ($this->debug) { $prefix = ($isOutput) ? 'Out' : 'In'; @@ -780,11 +746,11 @@ order by constraint_name, referenced_table_name, keyno"; # bind input params with sp_executesql: # see http://www.quest-pipelines.com/newsletter-v3/0402_F.htm # works only with sql server 7 and newer - $getIdentity = false; - if (!is_array($sql) && preg_match('/^\\s*insert/i', $sql)) { - $getIdentity = true; - $sql .= (preg_match('/;\\s*$/i', $sql) ? ' ' : '; ') . $this->identitySQL; - } + $getIdentity = false; + if (!is_array($sql) && preg_match('/^\\s*insert/i', $sql)) { + $getIdentity = true; + $sql .= (preg_match('/;\\s*$/i', $sql) ? ' ' : '; ') . $this->identitySQL; + } if (!is_array($sql)) $sql = $this->Prepare($sql); $params = ''; $decl = ''; @@ -805,16 +771,16 @@ order by constraint_name, referenced_table_name, keyno"; $decl .= "@P$i NVARCHAR($len)"; } - - if (substr($v,0,1) == "'" && substr($v,-1,1) == "'") + if(substr($v,0,1) == "'" && substr($v,-1,1) == "'") /* * String is already fully quoted */ $inputVar = $v; else - $inputVar = $this->qstr($v); + $inputVar = $db->this($v); - $params .= "@P$i=N" . $inputVar; + $params .= "@P$i=N" . $inputVar; + } else if (is_integer($v)) { $decl .= "@P$i INT"; $params .= "@P$i=".$v; @@ -833,20 +799,20 @@ order by constraint_name, referenced_table_name, keyno"; $decl = $this->qstr($decl); if ($this->debug) ADOConnection::outp("<font size=-1>sp_executesql N{$sql[1]},N$decl,$params</font>"); $rez = mssql_query("sp_executesql N{$sql[1]},N$decl,$params", $this->_connectionID); - if ($getIdentity) { - $arr = @mssql_fetch_row($rez); - $this->lastInsID = isset($arr[0]) ? $arr[0] : false; - @mssql_data_seek($rez, 0); - } + if ($getIdentity) { + $arr = @mssql_fetch_row($rez); + $this->lastInsID = isset($arr[0]) ? $arr[0] : false; + @mssql_data_seek($rez, 0); + } } else if (is_array($sql)) { # PrepareSP() $rez = mssql_execute($sql[1]); - $this->lastInsID = false; + $this->lastInsID = false; } else { $rez = mssql_query($sql,$this->_connectionID); - $this->lastInsID = false; + $this->lastInsID = false; } return $rez; } @@ -854,26 +820,47 @@ order by constraint_name, referenced_table_name, keyno"; // returns true or false function _close() { - if ($this->transCnt) $this->RollbackTrans(); - $rez = @mssql_close($this->_connectionID); + if ($this->transCnt) { + $this->RollbackTrans(); + } + if($this->_connectionID) { + $rez = mssql_close($this->_connectionID); + } $this->_connectionID = false; return $rez; } - // mssql uses a default date like Dec 30 2000 12:00AM - static function UnixDate($v) - { - return ADORecordSet_array_mssql::UnixDate($v); - } + - static function UnixTimeStamp($v) + /** + * Returns a substring of a varchar type field + * + * The SQL server version varies because the length is mandatory, so + * we append a reasonable string length + * + * @param string $fld The field to sub-string + * @param int $start The start point + * @param int $length An optional length + * + * @return The SQL text + */ + function substr($fld,$start,$length=0) { - return ADORecordSet_array_mssql::UnixTimeStamp($v); + if ($length == 0) + /* + * The length available to varchar is 2GB, but that makes no + * sense in a substring, so I'm going to arbitrarily limit + * the length to 1K, but you could change it if you want + */ + $length = 1024; + + $text = "SUBSTRING($fld,$start,$length)"; + return $text; } } /*-------------------------------------------------------------------------------------- - Class Name: Recordset + Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_mssql extends ADORecordSet { @@ -894,7 +881,7 @@ class ADORecordset_mssql extends ADORecordSet { } $this->fetchMode = $mode; - return parent::__construct($id,$mode); + return parent::__construct($id); } @@ -930,7 +917,7 @@ class ADORecordset_mssql extends ADORecordSet { } } - return $this->fields[$this->bind[strtoupper($colname)]]; + return $this->fields[$this->bind[strtoupper($colname)]]; } /* Returns: an object containing field information. @@ -970,7 +957,7 @@ class ADORecordset_mssql extends ADORecordSet { } else { if ($this->hasFetchAssoc) {// only for PHP 4.2.0 or later - $this->fields = @mssql_fetch_assoc($this->_queryID); + $this->fields = @mssql_fetch_assoc($this->_queryID); } else { $flds = @mssql_fetch_array($this->_queryID); if (is_array($flds)) { @@ -1027,9 +1014,9 @@ class ADORecordset_mssql extends ADORecordSet { $this->fields = @mssql_fetch_assoc($this->_queryID); else { $this->fields = @mssql_fetch_array($this->_queryID); - if (@is_array($$this->fields)) { + if (@is_array($this->fields)) { $fassoc = array(); - foreach($$this->fields as $k => $v) { + foreach($this->fields as $k => $v) { if (is_integer($k)) continue; $fassoc[$k] = $v; } @@ -1075,108 +1062,43 @@ class ADORecordset_mssql extends ADORecordSet { return true; } - // mssql uses a default date like Dec 30 2000 12:00AM - static function UnixDate($v) + /** + * Returns the maximum size of a MetaType C field. Because of the + * database design, SQL Server places no limits on the size of data inserted + * Although the actual limit is 2^31-1 bytes. + * + * @return int + */ + function charMax() { - return ADORecordSet_array_mssql::UnixDate($v); + return ADODB_STRINGMAX_NOLIMIT; } - static function UnixTimeStamp($v) + /** + * Returns the maximum size of a MetaType X field. Because of the + * database design, SQL Server places no limits on the size of data inserted + * Although the actual limit is 2^31-1 bytes. + * + * @return int + */ + function textMax() { - return ADORecordSet_array_mssql::UnixTimeStamp($v); + return ADODB_STRINGMAX_NOLIMIT; } } -class ADORecordSet_array_mssql extends ADORecordSet_array { - function __construct($id=-1,$mode=false) - { - parent::__construct($id,$mode); - } - - // mssql uses a default date like Dec 30 2000 12:00AM - static function UnixDate($v) - { - - if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v); - - global $ADODB_mssql_mths,$ADODB_mssql_date_order; - - //Dec 30 2000 12:00AM - if ($ADODB_mssql_date_order == 'dmy') { - if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) { - return parent::UnixDate($v); - } - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[1]; - $themth = substr(strtoupper($rr[2]),0,3); - } else { - if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) { - return parent::UnixDate($v); - } - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[2]; - $themth = substr(strtoupper($rr[1]),0,3); - } - $themth = $ADODB_mssql_mths[$themth]; - if ($themth <= 0) return false; - // h-m-s-MM-DD-YY - return mktime(0,0,0,$themth,$theday,$rr[3]); - } - - static function UnixTimeStamp($v) - { - - if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v); - - global $ADODB_mssql_mths,$ADODB_mssql_date_order; - - //Dec 30 2000 12:00AM - if ($ADODB_mssql_date_order == 'dmy') { - if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|" - ,$v, $rr)) return parent::UnixTimeStamp($v); - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[1]; - $themth = substr(strtoupper($rr[2]),0,3); - } else { - if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|" - ,$v, $rr)) return parent::UnixTimeStamp($v); - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[2]; - $themth = substr(strtoupper($rr[1]),0,3); - } - - $themth = $ADODB_mssql_mths[$themth]; - if ($themth <= 0) return false; - - switch (strtoupper($rr[6])) { - case 'P': - if ($rr[4]<12) $rr[4] += 12; - break; - case 'A': - if ($rr[4]==12) $rr[4] = 0; - break; - default: - break; - } - // h-m-s-MM-DD-YY - return mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]); - } -} +class ADORecordSet_array_mssql extends ADORecordSet_array {} /* Code Example 1: -select object_name(constid) as constraint_name, - object_name(fkeyid) as table_name, - col_name(fkeyid, fkey) as column_name, +select object_name(constid) as constraint_name, + object_name(fkeyid) as table_name, + col_name(fkeyid, fkey) as column_name, object_name(rkeyid) as referenced_table_name, - col_name(rkeyid, rkey) as referenced_column_name + col_name(rkeyid, rkey) as referenced_column_name from sysforeignkeys where object_name(fkeyid) = x order by constraint_name, table_name, referenced_table_name, keyno diff --git a/drivers/adodb-mssql_n.inc.php b/drivers/adodb-mssql_n.inc.php index 28b29bb2..0c44cad1 100644 --- a/drivers/adodb-mssql_n.inc.php +++ b/drivers/adodb-mssql_n.inc.php @@ -66,12 +66,12 @@ class ADODB_mssql_n extends ADODB_mssql { * and ODBTP) keeping SQL compatibility at ADOdb level (instead of hacking every project to add * the "N" notation when working against MSSQL. * - * The orginal note indicated that this hack should only be used if ALL the char-based columns + * The original note indicated that this hack should only be used if ALL the char-based columns * in your DB are of type nchar, nvarchar and ntext, but testing seems to indicate that SQL server * doesn't seem to care if the statement is used against char etc fields. * * @todo This function should raise an ADOdb error if one of the transformations fail - * + * * @param mixed $inboundData Either a string containing an SQL statement * or an array with resources from prepared statements * @@ -80,22 +80,22 @@ class ADODB_mssql_n extends ADODB_mssql { function _appendN($inboundData) { $inboundIsArray = false; - + if (is_array($inboundData)) { $inboundIsArray = true; $inboundArray = $inboundData; } else $inboundArray = (array)$inboundData; - + /* * All changes will be placed here */ $outboundArray = $inboundArray; - + foreach($inboundArray as $inboundKey=>$inboundValue) { - + if (is_resource($inboundValue)) { /* @@ -106,7 +106,7 @@ class ADODB_mssql_n extends ADODB_mssql { continue; } - + if (strpos($inboundValue, SINGLEQUOTE) === false) { /* @@ -121,11 +121,11 @@ class ADODB_mssql_n extends ADODB_mssql { * Check we haven't an odd number of single quotes (this can cause problems below * and should be considered one wrong SQL). Exit with debug info. */ - if ((substr_count($inboundValue, SINGLEQUOTE) & 1)) + if ((substr_count($inboundValue, SINGLEQUOTE) & 1)) { if ($this->debug) ADOConnection::outp("{$this->databaseType} internal transformation: not converted. Wrong number of quotes (odd)"); - + break; } @@ -136,9 +136,9 @@ class ADODB_mssql_n extends ADODB_mssql { $regexp = '/(\\\\' . SINGLEQUOTE . '[^' . SINGLEQUOTE . '])/'; if (preg_match($regexp, $inboundValue)) { - if ($this->debug) + if ($this->debug) ADOConnection::outp("{$this->databaseType} internal transformation: not converted. Found bad use of backslash + single quote"); - + break; } @@ -148,16 +148,16 @@ class ADODB_mssql_n extends ADODB_mssql { $pairs = array(); $regexp = '/(' . SINGLEQUOTE . SINGLEQUOTE . ')/'; preg_match_all($regexp, $inboundValue, $list_of_pairs); - + if ($list_of_pairs) { foreach (array_unique($list_of_pairs[0]) as $key=>$value) $pairs['<@#@#@PAIR-'.$key.'@#@#@>'] = $value; - - + + if (!empty($pairs)) $inboundValue = str_replace($pairs, array_keys($pairs), $inboundValue); - + } /* @@ -166,13 +166,13 @@ class ADODB_mssql_n extends ADODB_mssql { $literals = array(); $regexp = '/(N?' . SINGLEQUOTE . '.*?' . SINGLEQUOTE . ')/is'; preg_match_all($regexp, $inboundValue, $list_of_literals); - + if ($list_of_literals) { foreach (array_unique($list_of_literals[0]) as $key=>$value) $literals['<#@#@#LITERAL-'.$key.'#@#@#>'] = $value; - - + + if (!empty($literals)) $inboundValue = str_replace($literals, array_keys($literals), $inboundValue); } @@ -185,11 +185,11 @@ class ADODB_mssql_n extends ADODB_mssql { foreach ($literals as $key=>$value) { if (!is_numeric(trim($value, SINGLEQUOTE))) /* - * Non numeric string, prepend our dear N, whilst + * Non numeric string, prepend our dear N, whilst * Trimming potentially existing previous "N" */ - $literals[$key] = 'N' . trim($value, 'N'); - + $literals[$key] = 'N' . trim($value, 'N'); + } } @@ -198,7 +198,7 @@ class ADODB_mssql_n extends ADODB_mssql { */ if (!empty($literals)) $inboundValue = str_replace(array_keys($literals), $literals, $inboundValue); - + /* * Any pairs followed by N' must be switched to N' followed by those pairs @@ -211,40 +211,36 @@ class ADODB_mssql_n extends ADODB_mssql { */ if (!empty($pairs)) $inboundValue = str_replace(array_keys($pairs), $pairs, $inboundValue); - + /* * Print transformation if debug = on */ if (strcmp($inboundValue,$inboundArray[$inboundKey]) <> 0 && $this->debug) ADOConnection::outp("{$this->databaseType} internal transformation: {$inboundArray[$inboundKey]} to {$inboundValue}"); - + if (strcmp($inboundValue,$inboundArray[$inboundKey]) <> 0) /* * Place the transformed value into the outbound array */ $outboundArray[$inboundKey] = $inboundValue; } - + /* * Any transformations are in the $outboundArray */ if ($inboundIsArray) return $outboundArray; - + /* * We passed a string in originally */ return $outboundArray[0]; - + } } class ADORecordset_mssql_n extends ADORecordset_mssql { var $databaseType = "mssql_n"; - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-mssqlnative.inc.php b/drivers/adodb-mssqlnative.inc.php index 43feab61..9b3ba1a4 100644 --- a/drivers/adodb-mssqlnative.inc.php +++ b/drivers/adodb-mssqlnative.inc.php @@ -43,43 +43,6 @@ if (!function_exists('sqlsrv_log_set_subsystems')) { } } - -//---------------------------------------------------------------- -// MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002 -// and this causes tons of problems because localized versions of -// MSSQL will return the dates in dmy or mdy order; and also the -// month strings depends on what language has been configured. The -// following two variables allow you to control the localization -// settings - Ugh. -// -// MORE LOCALIZATION INFO -// ---------------------- -// To configure datetime, look for and modify sqlcommn.loc, -// typically found in c:\mssql\install -// Also read : -// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918 -// Alternatively use: -// CONVERT(char(12),datecol,120) -// -// Also if your month is showing as month-1, -// e.g. Jan 13, 2002 is showing as 13/0/2002, then see -// http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1 -// it's a localisation problem. -//---------------------------------------------------------------- - - -// has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc -if (ADODB_PHPVER >= 0x4300) { -// docs say 4.2.0, but testing shows only since 4.3.0 does it work! - ini_set('mssql.datetimeconvert',0); -} else { - global $ADODB_mssql_mths; // array, months must be upper-case - $ADODB_mssql_date_order = 'mdy'; - $ADODB_mssql_mths = array( - 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6, - 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); -} - class ADODB_mssqlnative extends ADOConnection { var $databaseType = "mssqlnative"; var $dataProvider = "mssqlnative"; @@ -124,8 +87,10 @@ class ADODB_mssqlnative extends ADOConnection { var $uniqueOrderBy = true; var $_bindInputArray = true; var $_dropSeqSQL = "drop table %s"; - var $connectionInfo = array(); + + var $connectionInfo = array('ReturnDatesAsStrings'=>true); var $cachedSchemaFlush = false; + var $sequences = false; var $mssql_version = ''; @@ -163,7 +128,7 @@ class ADODB_mssqlnative extends ADOConnection { } function ServerInfo() { - global $ADODB_FETCH_MODE; + global $ADODB_FETCH_MODE; static $arr = false; if (is_array($arr)) return $arr; @@ -189,11 +154,9 @@ class ADODB_mssqlnative extends ADOConnection { function _insertid() { - // SCOPE_IDENTITY() - // Returns the last IDENTITY value inserted into an IDENTITY column in - // the same scope. A scope is a module -- a stored procedure, trigger, - // function, or batch. Thus, two statements are in the same scope if - // they are in the same stored procedure, function, or batch. + $rez = sqlsrv_query($this->_connectionID,$this->identitySQL); + sqlsrv_fetch($rez); + $this->lastInsertID = sqlsrv_get_field($rez, 0); return $this->lastInsertID; } @@ -204,8 +167,6 @@ class ADODB_mssqlnative extends ADOConnection { } function GenID($seq='adodbseq',$start=1) { - if (!$this->mssql_version) - $this->ServerVersion(); switch($this->mssql_version){ case 9: case 10: @@ -219,9 +180,6 @@ class ADODB_mssqlnative extends ADOConnection { function CreateSequence($seq='adodbseq',$start=1) { - if (!$this->mssql_version) - $this->ServerVersion(); - switch($this->mssql_version){ case 9: case 10: @@ -231,7 +189,6 @@ class ADODB_mssqlnative extends ADOConnection { return $this->CreateSequence2012($seq, $start); break; } - } /** @@ -324,6 +281,20 @@ class ADODB_mssqlnative extends ADOConnection { if (!$col) $col = $this->sysTimeStamp; $s = ''; + $ConvertableFmt=array( + "m/d/Y"=>101,"m/d/y"=>101 // US + ,"Y.m.d"=>102,"y/m/d"=>102 // ANSI + ,"d/m/Y"=>103,"d/m/y"=>103 // French /english + ,"d.m.Y"=>104,"d.m.y"=>104 // German + ,"d-m-Y"=>105,"d-m-y"=>105 // Italian + ,"m-d-Y"=>110,"m-d-y"=>110 // US Dash + ,"Y/m/d"=>111,"y/m/d"=>111 // Japan + ,"Ymd"=>112,"ymd"=>112 // ISO + ,"H:i:s"=>108 // Time + ); + if(key_exists($fmt,$ConvertableFmt)) + return "convert (varchar ,$col,".$ConvertableFmt[$fmt].")"; + $len = strlen($fmt); for ($i=0; $i < $len; $i++) { if ($s) $s .= '+'; @@ -365,7 +336,9 @@ class ADODB_mssqlnative extends ADOConnection { case 'A': $s .= "substring(convert(char(19),$col,0),18,2)"; break; - + case 'l': + $s .= "datename(dw,$col)"; + break; default: if ($ch == '\\') { $i++; @@ -397,6 +370,7 @@ class ADODB_mssqlnative extends ADOConnection { sqlsrv_commit($this->_connectionID); return true; } + function RollbackTrans() { if ($this->transOff) return true; @@ -472,23 +446,59 @@ class ADODB_mssqlnative extends ADOConnection { // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { - if (!function_exists('sqlsrv_connect')) return null; - $connectionInfo = $this->connectionInfo; - $connectionInfo["Database"]=$argDatabasename; - $connectionInfo["UID"]=$argUsername; - $connectionInfo["PWD"]=$argPassword; + if (!function_exists('sqlsrv_connect')) + { + if ($this->debug) + ADOConnection::outp('Microsoft SQL Server native driver (mssqlnative) not installed'); + return null; + } - foreach ($this->connectionParameters as $parameter=>$value) - $connectionInfo[$parameter] = $value; + $connectionInfo = $this->connectionInfo; + $connectionInfo["Database"] = $argDatabasename; + if ((string)$argUsername != '' || (string)$argPassword != '') + { + /* + * If they pass either a userid or password, we assume + * SQL Server authentication + */ + $connectionInfo["UID"] = $argUsername; + $connectionInfo["PWD"] = $argPassword; + + if ($this->debug) + ADOConnection::outp('userid or password supplied, attempting connection with SQL Server Authentication'); + + } + else + { + /* + * If they don't pass either value, we won't add them to the + * connection parameters. This will then force an attempt + * to use windows authentication + */ + if ($this->debug) + ADOConnection::outp('No userid or password supplied, attempting connection with Windows Authentication'); + } + - if ($this->debug) ADOConnection::outp("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true)); - //if ($this->debug) ADOConnection::outp("<hr>_connectionID before: ".serialize($this->_connectionID)); - if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) { - if ($this->debug) ADOConnection::outp( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true)); + /* + * Now merge in the passed connection parameters setting + */ + foreach ($this->connectionParameters as $options) + { + foreach($options as $parameter=>$value) + $connectionInfo[$parameter] = $value; + } + + if ($this->debug) ADOConnection::outp("connecting to host: $argHostname params: ".var_export($connectionInfo,true)); + if(!($this->_connectionID = @sqlsrv_connect($argHostname,$connectionInfo))) + { + if ($this->debug) + ADOConnection::outp( 'Connection Failed: '.print_r( sqlsrv_errors(), true)); return false; } - //if ($this->debug) ADOConnection::outp(" _connectionID after: ".serialize($this->_connectionID)); - //if ($this->debug) ADOConnection::outp("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>"); + + $this->ServerVersion(); + return true; } @@ -502,10 +512,6 @@ class ADODB_mssqlnative extends ADOConnection { function Prepare($sql) { return $sql; // prepare does not work properly with bind parameters as bind parameters are managed by sqlsrv_prepare! - - $stmt = sqlsrv_prepare( $this->_connectionID, $sql); - if (!$stmt) return $sql; - return array($sql,$stmt); } // returns concatenated string @@ -565,7 +571,8 @@ class ADODB_mssqlnative extends ADOConnection { { $this->_errorMsg = false; - if (is_array($sql)) $sql = $sql[1]; + if (is_array($sql)) + $sql = $sql[1]; $insert = false; // handle native driver flaw for retrieving the last insert ID @@ -573,7 +580,14 @@ class ADODB_mssqlnative extends ADOConnection { $insert = true; $sql .= '; '.$this->identitySQL; // select scope_identity() } - if($inputarr) { + if($inputarr) + { + /* + * Ensure that the input array is numeric, as required by + * sqlsrv_query. If param() was used to create portable binds + * then the array might be associative + */ + $inputarr = array_values($inputarr); $rez = sqlsrv_query($this->_connectionID, $sql, $inputarr); } else { $rez = sqlsrv_query($this->_connectionID,$sql); @@ -581,38 +595,26 @@ class ADODB_mssqlnative extends ADOConnection { if ($this->debug) ADOConnection::outp("<hr>running query: ".var_export($sql,true)."<hr>input array: ".var_export($inputarr,true)."<hr>result: ".var_export($rez,true)); - if(!$rez) { + if(!$rez) $rez = false; - } else if ($insert) { - // retrieve the last insert ID (where applicable) - while ( sqlsrv_next_result($rez) ) { - sqlsrv_fetch($rez); - $this->lastInsertID = sqlsrv_get_field($rez, 0); - } - } + return $rez; } // returns true or false function _close() { - if ($this->transCnt) $this->RollbackTrans(); - $rez = @sqlsrv_close($this->_connectionID); + if ($this->transCnt) { + $this->RollbackTrans(); + } + if($this->_connectionID) { + $rez = sqlsrv_close($this->_connectionID); + } $this->_connectionID = false; return $rez; } - // mssql uses a default date like Dec 30 2000 12:00AM - static function UnixDate($v) - { - return ADORecordSet_array_mssqlnative::UnixDate($v); - } - - static function UnixTimeStamp($v) - { - return ADORecordSet_array_mssqlnative::UnixTimeStamp($v); - } - + function MetaIndexes($table,$primary=false, $owner = false) { $table = $this->qstr($table); @@ -670,7 +672,7 @@ class ADODB_mssqlnative extends ADOConnection { where upper(object_name(fkeyid)) = $table order by constraint_name, referenced_table_name, keyno"; - $constraints =& $this->GetArray($sql); + $constraints = $this->GetArray($sql); $ADODB_FETCH_MODE = $save; @@ -686,7 +688,11 @@ class ADODB_mssqlnative extends ADOConnection { foreach($arr as $k => $v) { foreach($v as $a => $b) { if ($upper) $a = strtoupper($a); - $arr2[$a] = $b; + if (is_array($arr2[$a])) { // a previous foreign key was define for this reference table, we merge the new one + $arr2[$a] = array_merge($arr2[$a], $b); + } else { + $arr2[$a] = $b; + } } } return $arr2; @@ -752,7 +758,9 @@ class ADODB_mssqlnative extends ADOConnection { } function MetaColumns($table, $upper=true, $schema=false){ - # start adg + /* + * A simple caching mechanism, to be replaced in ADOdb V6 + */ static $cached_columns = array(); if ($this->cachedSchemaFlush) $cached_columns = array(); @@ -760,10 +768,7 @@ class ADODB_mssqlnative extends ADOConnection { if (array_key_exists($table,$cached_columns)){ return $cached_columns[$table]; } - # end adg - if (!$this->mssql_version) - $this->ServerVersion(); $this->_findschema($table,$schema); if ($schema) { @@ -797,7 +802,7 @@ class ADODB_mssqlnative extends ADOConnection { $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; $fld->precision = $rs->fields[3]; - $fld->scale = $rs->fields[4]; + $fld->scale = $rs->fields[4]; $fld->not_null =!$rs->fields[5]; $fld->has_default = $rs->fields[6]; $fld->xtype = $rs->fields[7]; @@ -808,7 +813,7 @@ class ADODB_mssqlnative extends ADOConnection { $fld->type = $rs->fields['type']; $fld->max_length = $rs->fields['length']; $fld->precision = $rs->fields['precision']; - $fld->scale = $rs->fields['scale']; + $fld->scale = $rs->fields['scale']; $fld->not_null =!$rs->fields['nullable']; $fld->has_default = $rs->fields['default_value']; $fld->xtype = $rs->fields['xtype']; @@ -825,16 +830,130 @@ class ADODB_mssqlnative extends ADOConnection { } $rs->Close(); - # start adg $cached_columns[$table] = $retarr; - # end adg + return $retarr; } + /** + * Returns a substring of a varchar type field + * + * The SQL server version varies because the length is mandatory, so + * we append a reasonable string length + * + * @param string $fld The field to sub-string + * @param int $start The start point + * @param int $length An optional length + * + * @return The SQL text + */ + function substr($fld,$start,$length=0) + { + if ($length == 0) + /* + * The length available to varchar is 2GB, but that makes no + * sense in a substring, so I'm going to arbitrarily limit + * the length to 1K, but you could change it if you want + */ + $length = 1024; + + $text = "SUBSTRING($fld,$start,$length)"; + return $text; + } + + /** + * Returns the maximum size of a MetaType C field. Because of the + * database design, SQL Server places no limits on the size of data inserted + * Although the actual limit is 2^31-1 bytes. + * + * @return int + */ + function charMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + + /** + * Returns the maximum size of a MetaType X field. Because of the + * database design, SQL Server places no limits on the size of data inserted + * Although the actual limit is 2^31-1 bytes. + * + * @return int + */ + function textMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + /** + * Lists procedures, functions and methods in an array. + * + * @param string $procedureNamePattern (optional) + * @param string $catalog (optional) + * @param string $schemaPattern (optional) + + * @return array of stored objects in current database. + * + */ + public function metaProcedures($procedureNamePattern = null, $catalog = null, $schemaPattern = null) + { + + $metaProcedures = array(); + $procedureSQL = ''; + $catalogSQL = ''; + $schemaSQL = ''; + + if ($procedureNamePattern) + $procedureSQL = "AND ROUTINE_NAME LIKE " . strtoupper($this->qstr($procedureNamePattern)); + + if ($catalog) + $catalogSQL = "AND SPECIFIC_SCHEMA=" . strtoupper($this->qstr($catalog)); + + if ($schemaPattern) + $schemaSQL = "AND ROUTINE_SCHEMA LIKE {$this->qstr($schemaPattern)}"; + + + $fields = " ROUTINE_NAME,ROUTINE_TYPE,ROUTINE_SCHEMA,ROUTINE_CATALOG"; + + $SQL = "SELECT $fields + FROM {$this->database}.information_schema.routines + WHERE 1=1 + $procedureSQL + $catalogSQL + $schemaSQL + ORDER BY ROUTINE_NAME + "; + + $result = $this->execute($SQL); + + if (!$result) + return false; + while ($r = $result->fetchRow()){ + + if (!isset($r[0])) + /* + * Convert to numeric + */ + $r = array_values($r); + + $procedureName = $r[0]; + $schemaName = $r[2]; + $routineCatalog= $r[3]; + $metaProcedures[$procedureName] = array('type'=> $r[1], + 'catalog' => $routineCatalog, + 'schema' => $schemaName, + 'remarks' => '', + ); + + } + + return $metaProcedures; + + } + } /*-------------------------------------------------------------------------------------- - Class Name: Recordset + Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_mssqlnative extends ADORecordSet { @@ -844,6 +963,68 @@ class ADORecordset_mssqlnative extends ADORecordSet { var $fieldOffset = 0; // _mths works only in non-localised system + /* + * Holds a cached version of the metadata + */ + private $fieldObjects = false; + + /* + * Flags if we have retrieved the metadata + */ + private $fieldObjectsRetrieved = false; + + /* + * Cross-reference the objects by name for easy access + */ + private $fieldObjectsIndex = array(); + + + /* + * Cross references the dateTime objects for faster decoding + */ + private $dateTimeObjects = array(); + + /* + * flags that we have dateTimeObjects to handle + */ + private $hasDateTimeObjects = false; + + /* + * This is cross reference between how the types are stored + * in SQL Server and their english-language description + * -154 is a time field, see #432 + */ + private $_typeConversion = array( + -155 => 'datetimeoffset', + -154 => 'char', + -152 => 'xml', + -151 => 'udt', + -11 => 'uniqueidentifier', + -10 => 'ntext', + -9 => 'nvarchar', + -8 => 'nchar', + -7 => 'bit', + -6 => 'tinyint', + -5 => 'bigint', + -4 => 'image', + -3 => 'varbinary', + -2 => 'timestamp', + -1 => 'text', + 1 => 'char', + 2 => 'numeric', + 3 => 'decimal', + 4 => 'int', + 5 => 'smallint', + 6 => 'float', + 7 => 'real', + 12 => 'varchar', + 91 => 'date', + 93 => 'datetime' + ); + + + + function __construct($id,$mode=false) { if ($mode === false) { @@ -852,29 +1033,15 @@ class ADORecordset_mssqlnative extends ADORecordSet { } $this->fetchMode = $mode; - return parent::__construct($id,$mode); + parent::__construct($id); } function _initrs() { - global $ADODB_COUNTRECS; - # KMN # if ($this->connection->debug) ADOConnection::outp("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}"); - /*$retRowsAff = sqlsrv_rows_affected($this->_queryID);//"If you need to determine the number of rows a query will return before retrieving the actual results, appending a SELECT COUNT ... query would let you get that information, and then a call to next_result would move you to the "real" results." - ADOConnection::outp("rowsaff: ".serialize($retRowsAff)); - $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/ $this->_numOfRows = -1;//not supported - $fieldmeta = sqlsrv_field_metadata($this->_queryID); - $this->_numOfFields = ($fieldmeta)? count($fieldmeta):-1; - # KMN # if ($this->connection->debug) ADOConnection::outp("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}"); - /* - * Copy the oracle method and cache the metadata at init time - */ - if ($this->_numOfFields>0) { - $this->_fieldobjs = array(); - $max = $this->_numOfFields; - for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i); - } + // Cache the metadata right now + $this->_fetchField(); } @@ -901,6 +1068,7 @@ class ADORecordset_mssqlnative extends ADORecordSet { return; if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname]; + if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { @@ -912,79 +1080,74 @@ class ADORecordset_mssqlnative extends ADORecordSet { return $this->fields[$this->bind[strtoupper($colname)]]; } - /* Returns: an object containing field information. - Get column information in the Recordset object. fetchField() can be used in order to obtain information about - fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by - fetchField() is retrieved. - Designed By jcortinap#jc.com.mx + /** + * Returns: an object containing field information. + * + * Get column information in the Recordset object. fetchField() + * can be used in order to obtain information about fields in a + * certain query result. If the field offset isn't specified, + * the next field that wasn't yet retrieved by fetchField() + * is retrieved. + * + * $param int $fieldOffset (optional default=-1 for all + * @return mixed an ADOFieldObject, or array of objects */ - function _FetchField($fieldOffset = -1) + private function _fetchField($fieldOffset = -1) { - $_typeConversion = array( - -155 => 'datetimeoffset', - -154 => 'char', - -152 => 'xml', - -151 => 'udt', - -11 => 'uniqueidentifier', - -10 => 'ntext', - -9 => 'nvarchar', - -8 => 'nchar', - -7 => 'bit', - -6 => 'tinyint', - -5 => 'bigint', - -4 => 'image', - -3 => 'varbinary', - -2 => 'timestamp', - -1 => 'text', - 1 => 'char', - 2 => 'numeric', - 3 => 'decimal', - 4 => 'int', - 5 => 'smallint', - 6 => 'float', - 7 => 'real', - 12 => 'varchar', - 91 => 'date', - 93 => 'datetime' - ); - - $fa = @sqlsrv_field_metadata($this->_queryID); - if ($fieldOffset != -1) { - $fa = $fa[$fieldOffset]; - } - $false = false; - if (empty($fa)) { - $f = false;//PHP Notice: Only variable references should be returned by reference - } - else - { - // Convert to an object - $fa = array_change_key_case($fa, CASE_LOWER); - $fb = array(); - if ($fieldOffset != -1) - { - $fb = array( - 'name' => $fa['name'], - 'max_length' => $fa['size'], - 'column_source' => $fa['name'], - 'type' => $_typeConversion[$fa['type']] - ); + if ($this->fieldObjectsRetrieved){ + if ($this->fieldObjects) { + /* + * Already got the information + */ + if ($fieldOffset == -1) + return $this->fieldObjects; + else + return $this->fieldObjects[$fieldOffset]; } else - { - foreach ($fa as $key => $value) - { - $fb[] = array( - 'name' => $value['name'], - 'max_length' => $value['size'], - 'column_source' => $value['name'], - 'type' => $_typeConversion[$value['type']] - ); - } - } - $f = (object) $fb; + /* + * No metadata available + */ + return false; + } + + $this->fieldObjectsRetrieved = true; + /* + * Retrieve all metadata in one go. This is always returned as a + * numeric array. + */ + $fieldMetaData = sqlsrv_field_metadata($this->_queryID); + + if (!$fieldMetaData) + /* + * Not a statement that gives us metaData + */ + return false; + + $this->_numOfFields = count($fieldMetaData); + foreach ($fieldMetaData as $key=>$value) + { + + $fld = new ADOFieldObject; + /* + * Caution - keys are case-sensitive, must respect + * casing of values + */ + + $fld->name = $value['Name']; + $fld->max_length = $value['Size']; + $fld->column_source = $value['Name']; + $fld->type = $this->_typeConversion[$value['Type']]; + + $this->fieldObjects[$key] = $fld; + + $this->fieldObjectsIndex[$fld->name] = $key; + } - return $f; + if ($fieldOffset == -1) + return $this->fieldObjects; + + return $this->fieldObjects[$fieldOffset]; } /* @@ -992,12 +1155,16 @@ class ADORecordset_mssqlnative extends ADORecordSet { * into the _fieldobjs array once, to save multiple calls to the * sqlsrv_field_metadata function * + * @param int $fieldOffset (optional) + * + * @return adoFieldObject + * * @author KM Newnham * @date 02/20/2013 */ - function FetchField($fieldOffset = -1) + function fetchField($fieldOffset = -1) { - return $this->_fieldobjs[$fieldOffset]; + return $this->fieldObjects[$fieldOffset]; } function _seek($row) @@ -1008,76 +1175,52 @@ class ADORecordset_mssqlnative extends ADORecordSet { // speedup function MoveNext() { - //# KMN # if ($this->connection->debug) ADOConnection::outp("movenext()"); - //# KMN # if ($this->connection->debug) ADOConnection::outp("eof (beginning): ".$this->EOF); - if ($this->EOF) return false; + if ($this->EOF) + return false; $this->_currentRow++; - // # KMN # if ($this->connection->debug) ADOConnection::outp("_currentRow: ".$this->_currentRow); - if ($this->_fetch()) return true; + if ($this->_fetch()) + return true; $this->EOF = true; - //# KMN # if ($this->connection->debug) ADOConnection::outp("eof (end): ".$this->EOF); return false; } - - // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4 - // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot! function _fetch($ignore_fields=false) { - # KMN # if ($this->connection->debug) ADOConnection::outp("_fetch()"); if ($this->fetchMode & ADODB_FETCH_ASSOC) { - if ($this->fetchMode & ADODB_FETCH_NUM) { - //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: both"); + if ($this->fetchMode & ADODB_FETCH_NUM) $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_BOTH); - } else { - //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: assoc"); + else $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_ASSOC); - } - if (is_array($this->fields)) { - if (ADODB_ASSOC_CASE == 0) { - foreach($this->fields as $k=>$v) { - $this->fields[strtolower($k)] = $v; - } - } else if (ADODB_ASSOC_CASE == 1) { - foreach($this->fields as $k=>$v) { - $this->fields[strtoupper($k)] = $v; - } - } - } - } else { - //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: num"); - $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC); - } - if(is_array($this->fields) && array_key_exists(1,$this->fields) && !array_key_exists(0,$this->fields)) {//fix fetch numeric keys since they're not 0 based - $arrFixed = array(); - foreach($this->fields as $key=>$value) { - if(is_numeric($key)) { - $arrFixed[$key-1] = $value; - } else { - $arrFixed[$key] = $value; - } - } - //if($this->connection->debug) ADOConnection::outp("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true)); - $this->fields = $arrFixed; - } - if(is_array($this->fields)) { - foreach($this->fields as $key=>$value) { - if (is_object($value) && method_exists($value, 'format')) {//is DateTime object - $this->fields[$key] = $value->format("Y-m-d\TH:i:s\Z"); - } + if (is_array($this->fields)) + { + + if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) + $this->fields = array_change_key_case($this->fields,CASE_LOWER); + else if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER) + $this->fields = array_change_key_case($this->fields,CASE_UPPER); + } } - if($this->fields === null) $this->fields = false; - # KMN # if ($this->connection->debug) ADOConnection::outp("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false)); + else + $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC); + + if (!$this->fields) + return false; + return $this->fields; } - /* close() only needs to be called if you are worried about using too much memory while your script - is running. All associated result memory for the specified result identifier will automatically be freed. */ + /** + * close() only needs to be called if you are worried about using too much + * memory while your script is running. All associated result memory for + * the specified result identifier will automatically be freed. + * + * @return bool tru if we succeeded in closing down + */ function _close() { /* @@ -1091,101 +1234,14 @@ class ADORecordset_mssqlnative extends ADORecordSet { $this->_queryID = false; return $rez; } + return true; } - // mssql uses a default date like Dec 30 2000 12:00AM - static function UnixDate($v) - { - return ADORecordSet_array_mssqlnative::UnixDate($v); - } - - static function UnixTimeStamp($v) - { - return ADORecordSet_array_mssqlnative::UnixTimeStamp($v); - } } -class ADORecordSet_array_mssqlnative extends ADORecordSet_array { - function __construct($id=-1,$mode=false) - { - parent::__construct($id,$mode); - } - - // mssql uses a default date like Dec 30 2000 12:00AM - static function UnixDate($v) - { - - if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v); - - global $ADODB_mssql_mths,$ADODB_mssql_date_order; - - //Dec 30 2000 12:00AM - if ($ADODB_mssql_date_order == 'dmy') { - if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) { - return parent::UnixDate($v); - } - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[1]; - $themth = substr(strtoupper($rr[2]),0,3); - } else { - if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) { - return parent::UnixDate($v); - } - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[2]; - $themth = substr(strtoupper($rr[1]),0,3); - } - $themth = $ADODB_mssql_mths[$themth]; - if ($themth <= 0) return false; - // h-m-s-MM-DD-YY - return adodb_mktime(0,0,0,$themth,$theday,$rr[3]); - } - - static function UnixTimeStamp($v) - { - - if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v); - - global $ADODB_mssql_mths,$ADODB_mssql_date_order; - - //Dec 30 2000 12:00AM - if ($ADODB_mssql_date_order == 'dmy') { - if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|" - ,$v, $rr)) return parent::UnixTimeStamp($v); - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[1]; - $themth = substr(strtoupper($rr[2]),0,3); - } else { - if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|" - ,$v, $rr)) return parent::UnixTimeStamp($v); - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $theday = $rr[2]; - $themth = substr(strtoupper($rr[1]),0,3); - } - - $themth = $ADODB_mssql_mths[$themth]; - if ($themth <= 0) return false; - - switch (strtoupper($rr[6])) { - case 'P': - if ($rr[4]<12) $rr[4] += 12; - break; - case 'A': - if ($rr[4]==12) $rr[4] = 0; - break; - default: - break; - } - // h-m-s-MM-DD-YY - return adodb_mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]); - } -} +class ADORecordSet_array_mssqlnative extends ADORecordSet_array {} /* Code Example 1: diff --git a/drivers/adodb-mssqlpo.inc.php b/drivers/adodb-mssqlpo.inc.php index 5da97636..a8c5b3b9 100644 --- a/drivers/adodb-mssqlpo.inc.php +++ b/drivers/adodb-mssqlpo.inc.php @@ -32,10 +32,6 @@ class ADODB_mssqlpo extends ADODB_mssql { function PrepareSP($sql, $param = true) { - if (!$this->_has_mssql_init) { - ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); - return $sql; - } if (is_string($sql)) $sql = str_replace('||','+',$sql); $stmt = mssql_init($sql,$this->_connectionID); if (!$stmt) return $sql; @@ -51,8 +47,4 @@ class ADODB_mssqlpo extends ADODB_mssql { class ADORecordset_mssqlpo extends ADORecordset_mssql { var $databaseType = "mssqlpo"; - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-mysql.inc.php b/drivers/adodb-mysql.inc.php index 1d19a6c3..0dd4c5dd 100644 --- a/drivers/adodb-mysql.inc.php +++ b/drivers/adodb-mysql.inc.php @@ -9,10 +9,10 @@ Set tabs to 8. This driver only supports the original non-transactional MySQL driver. It - is deprected in PHP version 5.5 and removed in PHP version 7. It is deprecated + is deprecated in PHP version 5.5 and removed in PHP version 7. It is deprecated as of ADOdb version 5.20.0. Use the mysqli driver instead, which supports both transactional and non-transactional updates - + Requires mysql client. Works on Windows and Unix. 28 Feb 2001: MetaColumns bug fix - suggested by Freek Dijkstra (phpeverywhere@macfreek.com) @@ -51,14 +51,33 @@ class ADODB_mysql extends ADOConnection { var $nameQuote = '`'; /// string to use to quote identifiers and names var $compat323 = false; // true if compat with mysql 3.23 - function __construct() - { - if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_'; + /** + * ADODB_mysql constructor. + */ + public function __construct() { + if(version_compare(PHP_VERSION, '7.0.0', '>=')) { + $this->outp_throw( + 'mysql extension is not supported since PHP 7.0.0, use mysqli instead', + __METHOD__ + ); + die(1); // Stop execution even if not using Exceptions + } elseif(version_compare(PHP_VERSION, '5.5.0', '>=')) { + // If mysql extension is available just print a warning, + // otherwise die with an error message + if(function_exists('mysql_connect')) { + $this->outp('mysql extension is deprecated since PHP 5.5.0, consider using mysqli'); + } else { + $this->outp_throw( + 'mysql extension is not available, use mysqli instead', + __METHOD__ + ); + die(1); // Stop execution even if not using Exceptions + } + } } - // SetCharSet - switch the client encoding - function SetCharSet($charset_name) + function setCharSet($charset_name) { if (!function_exists('mysql_set_charset')) { return false; @@ -75,19 +94,19 @@ class ADODB_mysql extends ADOConnection { return true; } - function ServerInfo() + function serverInfo() { $arr['description'] = ADOConnection::GetOne("select version()"); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } - function IfNull( $field, $ifNull ) + function ifNull( $field, $ifNull ) { return " IFNULL($field, $ifNull) "; // if MySQL } - function MetaProcedures($NamePattern = false, $catalog = null, $schemaPattern = null) + function metaProcedures($NamePattern = false, $catalog = null, $schemaPattern = null) { // save old fetch mode global $ADODB_FETCH_MODE; @@ -153,7 +172,7 @@ class ADODB_mysql extends ADOConnection { * * @return array list of tables */ - function MetaTables($ttype=false,$showSchema=false,$mask=false) + function metaTables($ttype=false,$showSchema=false,$mask=false) { $save = $this->metaTablesSQL; if ($showSchema && is_string($showSchema)) { @@ -173,7 +192,7 @@ class ADODB_mysql extends ADOConnection { } - function MetaIndexes ($table, $primary = FALSE, $owner=false) + function metaIndexes ($table, $primary = FALSE, $owner=false) { // save old fetch mode global $ADODB_FETCH_MODE; @@ -232,10 +251,10 @@ class ADODB_mysql extends ADOConnection { if (is_null($s)) return 'NULL'; if (!$magic_quotes) { - if (ADODB_PHPVER >= 0x4300) { - if (is_resource($this->_connectionID)) - return "'".mysql_real_escape_string($s,$this->_connectionID)."'"; + if (is_resource($this->_connectionID)) { + return "'" . mysql_real_escape_string($s, $this->_connectionID) . "'"; } + if ($this->replaceQuote[0] == '\\'){ $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); } @@ -253,7 +272,7 @@ class ADODB_mysql extends ADOConnection { //return mysql_insert_id($this->_connectionID); } - function GetOne($sql,$inputarr=false) + function getOne($sql,$inputarr=false) { global $ADODB_GETONE_EOF; if ($this->compat323 == false && strncasecmp($sql,'sele',4) == 0) { @@ -269,7 +288,7 @@ class ADODB_mysql extends ADOConnection { return false; } - function BeginTrans() + function beginTrans() { if ($this->debug) ADOConnection::outp("Transactions not supported in 'mysql' driver. Use 'mysqlt' or 'mysqli' driver"); } @@ -287,7 +306,7 @@ class ADODB_mysql extends ADOConnection { var $_genSeq2SQL = "insert into %s values (%s)"; var $_dropSeqSQL = "drop table if exists %s"; - function CreateSequence($seqname='adodbseq',$startID=1) + function createSequence($seqname='adodbseq',$startID=1) { if (empty($this->_genSeqSQL)) return false; $u = strtoupper($seqname); @@ -298,7 +317,7 @@ class ADODB_mysql extends ADOConnection { } - function GenID($seqname='adodbseq',$startID=1) + function genID($seqname='adodbseq',$startID=1) { // post-nuke sets hasGenID to false if (!$this->hasGenID) return false; @@ -327,7 +346,7 @@ class ADODB_mysql extends ADOConnection { return $this->genID; } - function MetaDatabases() + function metaDatabases() { $qid = mysql_list_dbs($this->_connectionID); $arr = array(); @@ -343,7 +362,7 @@ class ADODB_mysql extends ADOConnection { // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) + function sqlDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = 'DATE_FORMAT('.$col.",'"; @@ -431,7 +450,7 @@ class ADODB_mysql extends ADOConnection { // returns concatenated string // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator - function Concat() + function concat() { $s = ""; $arr = func_get_args(); @@ -442,7 +461,7 @@ class ADODB_mysql extends ADOConnection { else return ''; } - function OffsetDate($dayFraction,$date=false) + function offsetDate($dayFraction,$date=false) { if (!$date) $date = $this->sysDate; @@ -455,19 +474,23 @@ class ADODB_mysql extends ADOConnection { // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { - if (!empty($this->port)) $argHostname .= ":".$this->port; + if (!empty($this->port)) + $argHostname .= ":".$this->port; - if (ADODB_PHPVER >= 0x4300) - $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, - $this->forceNewConnect,$this->clientFlags); - else if (ADODB_PHPVER >= 0x4200) - $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, - $this->forceNewConnect); - else - $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword); + $this->_connectionID = + mysql_connect($argHostname, + $argUsername, + $argPassword, + $this->forceNewConnect, + $this->clientFlags + ); + - if ($this->_connectionID === false) return false; - if ($argDatabasename) return $this->SelectDB($argDatabasename); + if ($this->_connectionID === false) + return false; + if ($argDatabasename) + return $this->SelectDB($argDatabasename); + return true; } @@ -476,13 +499,18 @@ class ADODB_mysql extends ADOConnection { { if (!empty($this->port)) $argHostname .= ":".$this->port; - if (ADODB_PHPVER >= 0x4300) - $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword,$this->clientFlags); - else - $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($this->autoRollback) $this->RollbackTrans(); - if ($argDatabasename) return $this->SelectDB($argDatabasename); + $this->_connectionID = + mysql_pconnect($argHostname, + $argUsername, + $argPassword, + $this->clientFlags); + + if ($this->_connectionID === false) + return false; + if ($this->autoRollback) + $this->RollbackTrans(); + if ($argDatabasename) + return $this->SelectDB($argDatabasename); return true; } @@ -492,7 +520,7 @@ class ADODB_mysql extends ADOConnection { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); } - function MetaColumns($table, $normalize=true) + function metaColumns($table, $normalize=true) { $this->_findschema($table,$schema); if ($schema) { @@ -572,7 +600,7 @@ class ADODB_mysql extends ADOConnection { } // returns true or false - function SelectDB($dbName) + function selectDB($dbName) { $this->database = $dbName; $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions @@ -583,7 +611,7 @@ class ADODB_mysql extends ADOConnection { } // parameters use PostgreSQL convention, not MySQL - function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0) + function selectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0) { $nrows = (int) $nrows; $offset = (int) $offset; @@ -613,7 +641,7 @@ class ADODB_mysql extends ADOConnection { } /* Returns: the last error message from previous database operation */ - function ErrorMsg() + function errorMsg() { if ($this->_logsql) return $this->_errorMsg; @@ -623,7 +651,7 @@ class ADODB_mysql extends ADOConnection { } /* Returns: the last error number from previous database operation */ - function ErrorNo() + function errorNo() { if ($this->_logsql) return $this->_errorCode; if (empty($this->_connectionID)) return @mysql_errno(); @@ -643,7 +671,7 @@ class ADODB_mysql extends ADOConnection { /* * Maximum size of C field */ - function CharMax() + function charMax() { return 255; } @@ -651,13 +679,13 @@ class ADODB_mysql extends ADOConnection { /* * Maximum size of X field */ - function TextMax() + function textMax() { return 4294967295; } // "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx> - function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) + function metaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) { global $ADODB_FETCH_MODE; if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true; @@ -743,7 +771,7 @@ class ADORecordSet_mysql extends ADORecordSet{ $this->_numOfFields = @mysql_num_fields($this->_queryID); } - function FetchField($fieldOffset = -1) + function fetchField($fieldOffset = -1) { if ($fieldOffset != -1) { $o = @mysql_fetch_field($this->_queryID, $fieldOffset); @@ -761,7 +789,7 @@ class ADORecordSet_mysql extends ADORecordSet{ return $o; } - function GetRowAssoc($upper = ADODB_ASSOC_CASE) + function getRowAssoc($upper = ADODB_ASSOC_CASE) { if ($this->fetchMode == MYSQL_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) { $row = $this->fields; @@ -773,7 +801,7 @@ class ADORecordSet_mysql extends ADORecordSet{ } /* Use associative array to get fields array */ - function Fields($colname) + function fields($colname) { // added @ by "Michael William Miller" <mille562@pilot.msu.edu> if ($this->fetchMode != MYSQL_NUM) return @$this->fields[$colname]; @@ -794,10 +822,8 @@ class ADORecordSet_mysql extends ADORecordSet{ return @mysql_data_seek($this->_queryID,$row); } - function MoveNext() + function moveNext() { - //return adodb_movenext($this); - //if (defined('ADODB_EXTENSION')) return adodb_movenext($this); if (@$this->fields = mysql_fetch_array($this->_queryID,$this->fetchMode)) { $this->_updatefields(); $this->_currentRow += 1; @@ -822,7 +848,7 @@ class ADORecordSet_mysql extends ADORecordSet{ $this->_queryID = false; } - function MetaType($t,$len=-1,$fieldobj=false) + function metaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; @@ -872,19 +898,18 @@ class ADORecordSet_mysql extends ADORecordSet{ if (!empty($fieldobj->primary_key)) return 'R'; else return 'I'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } } +/** + * Class ADORecordSet_ext_mysql + */ class ADORecordSet_ext_mysql extends ADORecordSet_mysql { - function __construct($queryID,$mode=false) - { - parent::__construct($queryID,$mode); - } - function MoveNext() + function moveNext() { return @adodb_movenext($this); } diff --git a/drivers/adodb-mysqli.inc.php b/drivers/adodb-mysqli.inc.php index 45f6d1ee..0bc3221c 100644 --- a/drivers/adodb-mysqli.inc.php +++ b/drivers/adodb-mysqli.inc.php @@ -12,7 +12,7 @@ and non-transactional table types. You can use this as a drop-in replacement for both the mysql and mysqlt drivers. As of ADOdb Version 5.20.0, all other native MySQL drivers are deprecated - + Requires mysql client. Works on Windows and Unix. 21 October 2003: MySQLi extension implementation by Arjen de Rijke (a.de.rijke@xs4all.nl) @@ -20,18 +20,20 @@ Based on adodb 3.40 */ // security - hide paths -if (!defined('ADODB_DIR')) die(); - -if (! defined("_ADODB_MYSQLI_LAYER")) { - define("_ADODB_MYSQLI_LAYER", 1 ); +if (!defined('ADODB_DIR')) { + die(); +} - // PHP5 compat... - if (! defined("MYSQLI_BINARY_FLAG")) define("MYSQLI_BINARY_FLAG", 128); - if (!defined('MYSQLI_READ_DEFAULT_GROUP')) define('MYSQLI_READ_DEFAULT_GROUP',1); +if (!defined("_ADODB_MYSQLI_LAYER")) { + define("_ADODB_MYSQLI_LAYER", 1); - // disable adodb extension - currently incompatible. - global $ADODB_EXTENSION; $ADODB_EXTENSION = false; +// PHP5 compat... +if (! defined("MYSQLI_BINARY_FLAG")) define("MYSQLI_BINARY_FLAG", 128); +if (!defined('MYSQLI_READ_DEFAULT_GROUP')) define('MYSQLI_READ_DEFAULT_GROUP',1); +/** + * Class ADODB_mysqli + */ class ADODB_mysqli extends ADOConnection { var $databaseType = 'mysqli'; var $dataProvider = 'mysql'; @@ -62,31 +64,58 @@ class ADODB_mysqli extends ADOConnection { var $optionFlags = array(array(MYSQLI_READ_DEFAULT_GROUP,0)); var $arrayClass = 'ADORecordSet_array_mysqli'; var $multiQuery = false; + var $ssl_key = null; + var $ssl_cert = null; + var $ssl_ca = null; + var $ssl_capath = null; + var $ssl_cipher = null; - function __construct() - { - // if(!extension_loaded("mysqli")) - //trigger_error("You must have the mysqli extension installed.", E_USER_ERROR); - } + /** + * Tells the insert_id method how to obtain the last value, depending on whether + * we are using a stored procedure or not + */ + private $usePreparedStatement = false; + private $useLastInsertStatement = false; - function SetTransactionMode( $transaction_mode ) + /** + * Sets the isolation level of a transaction. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:settransactionmode + * + * @param string $transaction_mode The transaction mode to set. + * + * @return void + */ + function SetTransactionMode($transaction_mode) { $this->_transmode = $transaction_mode; if (empty($transaction_mode)) { - $this->Execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ'); + $this->execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; - $this->Execute("SET SESSION TRANSACTION ".$transaction_mode); + $this->execute("SET SESSION TRANSACTION ".$transaction_mode); } - // returns true or false - // To add: parameter int $port, - // parameter string $socket - function _connect($argHostname = NULL, - $argUsername = NULL, - $argPassword = NULL, - $argDatabasename = NULL, $persist=false) + /** + * Connect to a database. + * + * @todo add: parameter int $port, parameter string $socket + * + * @param string|null $argHostname (Optional) The host to connect to. + * @param string|null $argUsername (Optional) The username to connect as. + * @param string|null $argPassword (Optional) The password to connect with. + * @param string|null $argDatabasename (Optional) The name of the database to start in when connected. + * @param bool $persist (Optional) Whether or not to use a persistent connection. + * + * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension + * isn't currently loaded. + */ + function _connect($argHostname = null, + $argUsername = null, + $argPassword = null, + $argDatabasename = null, + $persist = false) { if(!extension_loaded("mysqli")) { return null; @@ -96,7 +125,7 @@ class ADODB_mysqli extends ADOConnection { if (is_null($this->_connectionID)) { // mysqli_init only fails if insufficient memory if ($this->debug) { - ADOConnection::outp("mysqli_init() failed : " . $this->ErrorMsg()); + ADOConnection::outp("mysqli_init() failed : " . $this->errorMsg()); } return false; } @@ -105,12 +134,29 @@ class ADODB_mysqli extends ADOConnection { read connection options from the standard mysql configuration file /etc/my.cnf - "Bastien Duclaux" <bduclaux#yahoo.com> */ + $this->optionFlags = array(); foreach($this->optionFlags as $arr) { mysqli_options($this->_connectionID,$arr[0],$arr[1]); } - //http ://php.net/manual/en/mysqli.persistconns.php - if ($persist && PHP_VERSION > 5.2 && strncmp($argHostname,'p:',2) != 0) $argHostname = 'p:'.$argHostname; + /* + * Now merge in the standard connection parameters setting + */ + foreach ($this->connectionParameters as $options) + { + foreach($options as $k=>$v) + $ok = mysqli_options($this->_connectionID,$k,$v); + } + + //https://php.net/manual/en/mysqli.persistconns.php + if ($persist && strncmp($argHostname,'p:',2) != 0) { + $argHostname = 'p:' . $argHostname; + } + + // SSL Connections for MySQLI + if ($this->ssl_key || $this->ssl_cert || $this->ssl_ca || $this->ssl_capath || $this->ssl_cipher) { + mysqli_ssl_set($this->_connectionID, $this->ssl_key, $this->ssl_cert, $this->ssl_ca, $this->ssl_capath, $this->ssl_cipher); + } #if (!empty($this->port)) $argHostname .= ":".$this->port; $ok = @mysqli_real_connect($this->_connectionID, @@ -124,105 +170,185 @@ class ADODB_mysqli extends ADOConnection { $this->clientFlags); if ($ok) { - if ($argDatabasename) return $this->SelectDB($argDatabasename); + if ($argDatabasename) return $this->selectDB($argDatabasename); return true; } else { if ($this->debug) { - ADOConnection::outp("Could not connect : " . $this->ErrorMsg()); + ADOConnection::outp("Could not connect : " . $this->errorMsg()); } $this->_connectionID = null; return false; } } - // returns true or false - // How to force a persistent connection + /** + * Connect to a database with a persistent connection. + * + * @param string|null $argHostname The host to connect to. + * @param string|null $argUsername The username to connect as. + * @param string|null $argPassword The password to connect with. + * @param string|null $argDatabasename The name of the database to start in when connected. + * + * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension + * isn't currently loaded. + */ function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true); } - // When is this used? Close old connection first? - // In _connect(), check $this->forceNewConnect? + /** + * Connect to a database, whilst setting $this->forceNewConnect to true. + * + * When is this used? Close old connection first? + * In _connect(), check $this->forceNewConnect? + * + * @param string|null $argHostname The host to connect to. + * @param string|null $argUsername The username to connect as. + * @param string|null $argPassword The password to connect with. + * @param string|null $argDatabasename The name of the database to start in when connected. + * + * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension + * isn't currently loaded. + */ function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { $this->forceNewConnect = true; return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); } - function IfNull( $field, $ifNull ) + /** + * Replaces a null value with a specified replacement. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:ifnull + * + * @param mixed $field The field in the table to check. + * @param mixed $ifNull The value to replace the null value with if it is found. + * + * @return string + */ + function IfNull($field, $ifNull) { - return " IFNULL($field, $ifNull) "; // if MySQL + return " IFNULL($field, $ifNull) "; } - // do not use $ADODB_COUNTRECS - function GetOne($sql,$inputarr=false) + /** + * Retrieves the first column of the first matching row of an executed SQL statement. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:getone + * + * @param string $sql The SQL to execute. + * @param bool|array $inputarr (Optional) An array containing any required SQL parameters, or false if none needed. + * + * @return bool|array|null + */ + function GetOne($sql, $inputarr = false) { global $ADODB_GETONE_EOF; $ret = false; - $rs = $this->Execute($sql,$inputarr); + $rs = $this->execute($sql,$inputarr); if ($rs) { if ($rs->EOF) $ret = $ADODB_GETONE_EOF; else $ret = reset($rs->fields); - $rs->Close(); + $rs->close(); } return $ret; } + /** + * Get information about the current MySQL server. + * + * @return array + */ function ServerInfo() { - $arr['description'] = $this->GetOne("select version()"); + $arr['description'] = $this->getOne("select version()"); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } - + /** + * Begins a granular transaction. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:begintrans + * + * @return bool Always returns true. + */ function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; - //$this->Execute('SET AUTOCOMMIT=0'); + //$this->execute('SET AUTOCOMMIT=0'); mysqli_autocommit($this->_connectionID, false); - $this->Execute('BEGIN'); + $this->execute('BEGIN'); return true; } - function CommitTrans($ok=true) + /** + * Commits a granular transaction. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:committrans + * + * @param bool $ok (Optional) If false, will rollback the transaction instead. + * + * @return bool Always returns true. + */ + function CommitTrans($ok = true) { if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); + if (!$ok) return $this->rollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('COMMIT'); + $this->execute('COMMIT'); - //$this->Execute('SET AUTOCOMMIT=1'); + //$this->execute('SET AUTOCOMMIT=1'); mysqli_autocommit($this->_connectionID, true); return true; } + /** + * Rollback a smart transaction. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rollbacktrans + * + * @return bool Always returns true. + */ function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('ROLLBACK'); - //$this->Execute('SET AUTOCOMMIT=1'); + $this->execute('ROLLBACK'); + //$this->execute('SET AUTOCOMMIT=1'); mysqli_autocommit($this->_connectionID, true); return true; } - function RowLock($tables,$where='',$col='1 as adodbignore') + /** + * Lock a table row for a duration of a transaction. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rowlock + * + * @param string $tables The table(s) to lock rows for. + * @param string $where (Optional) The WHERE clause to use to determine which rows to lock. + * @param string $col (Optional) The columns to select. + * + * @return bool True if the locking SQL statement executed successfully, otherwise false. + */ + function RowLock($tables, $where = '', $col = '1 as adodbignore') { - if ($this->transCnt==0) $this->BeginTrans(); + if ($this->transCnt==0) $this->beginTrans(); if ($where) $where = ' where '.$where; - $rs = $this->Execute("select $col from $tables $where for update"); + $rs = $this->execute("select $col from $tables $where for update"); return !empty($rs); } /** - * Quotes a string to be sent to the database - * When there is no active connection, + * Appropriately quotes strings with ' characters for insertion into the database. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:qstr + * * @param string $s The string to quote * @param boolean $magic_quotes If false, use mysqli_real_escape_string() * if you are quoting a string extracted from a POST/GET variable, @@ -230,6 +356,7 @@ class ADODB_mysqli extends ADOConnection { * ensure that the variable is not quoted twice, once by qstr() and * once by the magic_quotes_gpc. * Eg. $s = $db->qstr(_GET['name'],get_magic_quotes_gpc()); + * * @return string Quoted string */ function qstr($s, $magic_quotes = false) @@ -238,7 +365,7 @@ class ADODB_mysqli extends ADOConnection { if (!$magic_quotes) { // mysqli_real_escape_string() throws a warning when the given // connection is invalid - if (PHP_VERSION >= 5 && $this->_connectionID) { + if ($this->_connectionID) { return "'" . mysqli_real_escape_string($this->_connectionID, $s) . "'"; } @@ -252,26 +379,45 @@ class ADODB_mysqli extends ADOConnection { return "'$s'"; } + /** + * Return the AUTO_INCREMENT id of the last row that has been inserted or updated in a table. + * + * @return int|string + */ function _insertid() { - $result = @mysqli_insert_id($this->_connectionID); + // mysqli_insert_id does not return the last_insert_id if called after + // execution of a stored procedure so we execute this instead. + if ($this->useLastInsertStatement) + $result = ADOConnection::getOne('SELECT LAST_INSERT_ID()'); + else + $result = @mysqli_insert_id($this->_connectionID); + if ($result == -1) { - if ($this->debug) ADOConnection::outp("mysqli_insert_id() failed : " . $this->ErrorMsg()); + if ($this->debug) + ADOConnection::outp("mysqli_insert_id() failed : " . $this->errorMsg()); } + // reset prepared statement flags + $this->usePreparedStatement = false; + $this->useLastInsertStatement = false; return $result; } - // Only works for INSERT, UPDATE and DELETE query's + /** + * Returns how many rows were effected by the most recently executed SQL statement. + * Only works for INSERT, UPDATE and DELETE queries. + * + * @return int The number of rows affected. + */ function _affectedrows() { $result = @mysqli_affected_rows($this->_connectionID); if ($result == -1) { - if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : " . $this->ErrorMsg()); + if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : " . $this->errorMsg()); } return $result; } - // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html // Reference on Last_Insert_ID on the recommended way to simulate sequences var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; var $_genSeqSQL = "create table if not exists %s (id int not null)"; @@ -279,60 +425,96 @@ class ADODB_mysqli extends ADOConnection { var $_genSeq2SQL = "insert into %s values (%s)"; var $_dropSeqSQL = "drop table if exists %s"; - function CreateSequence($seqname='adodbseq',$startID=1) + /** + * Creates a sequence in the database. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:createsequence + * + * @param string $seqname The sequence name. + * @param int $startID The start id. + * + * @return ADORecordSet|bool A record set if executed successfully, otherwise false. + */ + function CreateSequence($seqname = 'adodbseq', $startID = 1) { if (empty($this->_genSeqSQL)) return false; - $u = strtoupper($seqname); - $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); + $ok = $this->execute(sprintf($this->_genSeqSQL,$seqname)); if (!$ok) return false; - return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); + return $this->execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); } - function GenID($seqname='adodbseq',$startID=1) + /** + * A portable method of creating sequence numbers. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:genid + * + * @param string $seqname (Optional) The name of the sequence to use. + * @param int $startID (Optional) The point to start at in the sequence. + * + * @return bool|int|string + */ + function GenID($seqname = 'adodbseq', $startID = 1) { // post-nuke sets hasGenID to false if (!$this->hasGenID) return false; $getnext = sprintf($this->_genIDSQL,$seqname); $holdtransOK = $this->_transOK; // save the current status - $rs = @$this->Execute($getnext); + $rs = @$this->execute($getnext); if (!$rs) { if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset - $u = strtoupper($seqname); - $this->Execute(sprintf($this->_genSeqSQL,$seqname)); - $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL,$seqname)); - if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); - $rs = $this->Execute($getnext); + $this->execute(sprintf($this->_genSeqSQL,$seqname)); + $cnt = $this->getOne(sprintf($this->_genSeqCountSQL,$seqname)); + if (!$cnt) $this->execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); + $rs = $this->execute($getnext); } if ($rs) { $this->genID = mysqli_insert_id($this->_connectionID); - $rs->Close(); + if ($this->genID == 0) { + $getnext = "select LAST_INSERT_ID() from " . $seqname; + $rs = $this->execute($getnext); + $this->genID = (int)$rs->fields[0]; + } + $rs->close(); } else $this->genID = 0; return $this->genID; } + /** + * Return a list of all visible databases except the 'mysql' database. + * + * @return array|false An array of database names, or false if the query failed. + */ function MetaDatabases() { $query = "SHOW DATABASES"; - $ret = $this->Execute($query); + $ret = $this->execute($query); if ($ret && is_object($ret)){ $arr = array(); while (!$ret->EOF){ - $db = $ret->Fields('Database'); + $db = $ret->fields('Database'); if ($db != 'mysql') $arr[] = $db; - $ret->MoveNext(); + $ret->moveNext(); } return $arr; } return $ret; } - - function MetaIndexes ($table, $primary = FALSE, $owner = false) + /** + * Get a list of indexes on the specified table. + * + * @param string $table The name of the table to get indexes for. + * @param bool $primary (Optional) Whether or not to include the primary key. + * @param bool $owner (Optional) Unused. + * + * @return array|bool An array of the indexes, or false if the query to get the indexes failed. + */ + function MetaIndexes($table, $primary = false, $owner = false) { // save old fetch mode global $ADODB_FETCH_MODE; @@ -341,15 +523,15 @@ class ADODB_mysqli extends ADOConnection { $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { - $savem = $this->SetFetchMode(FALSE); + $savem = $this->setFetchMode(FALSE); } // get index details - $rs = $this->Execute(sprintf('SHOW INDEXES FROM %s',$table)); + $rs = $this->execute(sprintf('SHOW INDEXES FROM %s',$table)); // restore fetchmode if (isset($savem)) { - $this->SetFetchMode($savem); + $this->setFetchMode($savem); } $ADODB_FETCH_MODE = $save; @@ -360,7 +542,7 @@ class ADODB_mysqli extends ADOConnection { $indexes = array (); // parse index data into array - while ($row = $rs->FetchRow()) { + while ($row = $rs->fetchRow()) { if ($primary == FALSE AND $row[2] == 'PRIMARY') { continue; } @@ -384,9 +566,17 @@ class ADODB_mysqli extends ADOConnection { return $indexes; } - - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) + /** + * Returns a portably-formatted date string from a timestamp database column. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:sqldate + * + * @param string $fmt The date format to use. + * @param string|bool $col (Optional) The table column to date format, or if false, use NOW(). + * + * @return bool|string The SQL DATE_FORMAT() string, or false if the provided date format was empty. + */ + function SQLDate($fmt, $col = false) { if (!$col) $col = $this->sysTimeStamp; $s = 'DATE_FORMAT('.$col.",'"; @@ -463,11 +653,15 @@ class ADODB_mysqli extends ADOConnection { return $s; } - // returns concatenated string - // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator + /** + * Returns a database-specific concatenation of strings. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:concat + * + * @return string + */ function Concat() { - $s = ""; $arr = func_get_args(); // suggestion by andrew005@mnogo.ru @@ -476,8 +670,17 @@ class ADODB_mysqli extends ADOConnection { else return ''; } - // dayFraction is a day in floating point - function OffsetDate($dayFraction,$date=false) + /** + * Creates a portable date offset field, for use in SQL statements. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:offsetdate + * + * @param float $dayFraction A day in floating point + * @param string|bool $date (Optional) The date to offset. If false, uses CURDATE() + * + * @return string + */ + function OffsetDate($dayFraction, $date = false) { if (!$date) $date = $this->sysDate; @@ -487,17 +690,25 @@ class ADODB_mysqli extends ADOConnection { // return "from_unixtime(unix_timestamp($date)+$fraction)"; } + /** + * Returns information about stored procedures and stored functions. + * + * @param string|bool $NamePattern (Optional) Only look for procedures/functions with a name matching this pattern. + * @param null $catalog (Optional) Unused. + * @param null $schemaPattern (Optional) Unused. + * + * @return array + */ function MetaProcedures($NamePattern = false, $catalog = null, $schemaPattern = null) { // save old fetch mode global $ADODB_FETCH_MODE; - $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { - $savem = $this->SetFetchMode(FALSE); + $savem = $this->setFetchMode(FALSE); } $procedures = array (); @@ -508,11 +719,11 @@ class ADODB_mysqli extends ADOConnection { if ($NamePattern) { $likepattern = " LIKE '".$NamePattern."'"; } - $rs = $this->Execute('SHOW PROCEDURE STATUS'.$likepattern); + $rs = $this->execute('SHOW PROCEDURE STATUS'.$likepattern); if (is_object($rs)) { // parse index data into array - while ($row = $rs->FetchRow()) { + while ($row = $rs->fetchRow()) { $procedures[$row[1]] = array( 'type' => 'PROCEDURE', 'catalog' => '', @@ -522,10 +733,10 @@ class ADODB_mysqli extends ADOConnection { } } - $rs = $this->Execute('SHOW FUNCTION STATUS'.$likepattern); + $rs = $this->execute('SHOW FUNCTION STATUS'.$likepattern); if (is_object($rs)) { // parse index data into array - while ($row = $rs->FetchRow()) { + while ($row = $rs->fetchRow()) { $procedures[$row[1]] = array( 'type' => 'FUNCTION', 'catalog' => '', @@ -537,7 +748,7 @@ class ADODB_mysqli extends ADOConnection { // restore fetchmode if (isset($savem)) { - $this->SetFetchMode($savem); + $this->setFetchMode($savem); } $ADODB_FETCH_MODE = $save; @@ -547,13 +758,13 @@ class ADODB_mysqli extends ADOConnection { /** * Retrieves a list of tables based on given criteria * - * @param string $ttype Table type = 'TABLE', 'VIEW' or false=both (default) - * @param string $showSchema schema name, false = current schema (default) - * @param string $mask filters the table by name + * @param string|bool $ttype (Optional) Table type = 'TABLE', 'VIEW' or false=both (default) + * @param string|bool $showSchema (Optional) schema name, false = current schema (default) + * @param string|bool $mask (Optional) filters the table by name * * @return array list of tables */ - function MetaTables($ttype=false,$showSchema=false,$mask=false) + function MetaTables($ttype = false, $showSchema = false, $mask = false) { $save = $this->metaTablesSQL; if ($showSchema && is_string($showSchema)) { @@ -566,26 +777,43 @@ class ADODB_mysqli extends ADOConnection { $mask = $this->qstr($mask); $this->metaTablesSQL .= " AND table_name LIKE $mask"; } - $ret = ADOConnection::MetaTables($ttype,$showSchema); + $ret = ADOConnection::metaTables($ttype,$showSchema); $this->metaTablesSQL = $save; return $ret; } - // "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx> - function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) + /** + * Return information about a table's foreign keys. + * + * @param string $table The name of the table to get the foreign keys for. + * @param string|bool $owner (Optional) The database the table belongs to, or false to assume the current db. + * @param string|bool $upper (Optional) Force uppercase table name on returned array keys. + * @param bool $associative (Optional) Whether to return an associate or numeric array. + * + * @return array|bool An array of foreign keys, or false no foreign keys could be found. + */ + function MetaForeignKeys($table, $owner = false, $upper = false, $associative = false) { - global $ADODB_FETCH_MODE; - if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true; + global $ADODB_FETCH_MODE; + + if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC + || $this->fetchMode == ADODB_FETCH_ASSOC) + $associative = true; + + $savem = $ADODB_FETCH_MODE; + $this->setFetchMode(ADODB_FETCH_ASSOC); if ( !empty($owner) ) { $table = "$owner.$table"; } + $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table)); - if ($associative) { - $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"]; - } else $create_sql = $a_create_table[1]; + + $this->setFetchMode($savem); + + $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"]; $matches = array(); @@ -618,7 +846,15 @@ class ADODB_mysqli extends ADOConnection { return $foreign_keys; } - function MetaColumns($table, $normalize=true) + /** + * Return an array of information about a table's columns. + * + * @param string $table The name of the table to get the column info for. + * @param bool $normalize (Optional) Unused. + * + * @return ADOFieldObject[]|bool An array of info for each column, or false if it could not determine the info. + */ + function MetaColumns($table, $normalize = true) { $false = false; if (!$this->metaColumnsSQL) @@ -628,9 +864,9 @@ class ADODB_mysqli extends ADOConnection { $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) - $savem = $this->SetFetchMode(false); - $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); - if (isset($savem)) $this->SetFetchMode($savem); + $savem = $this->setFetchMode(false); + $rs = $this->execute(sprintf($this->metaColumnsSQL,$table)); + if (isset($savem)) $this->setFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!is_object($rs)) return $false; @@ -682,14 +918,22 @@ class ADODB_mysqli extends ADOConnection { } else { $retarr[strtoupper($fld->name)] = $fld; } - $rs->MoveNext(); + $rs->moveNext(); } - $rs->Close(); + $rs->close(); return $retarr; } - // returns true or false + /** + * Select which database to connect to. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:selectdb + * + * @param string $dbName The name of the database to select. + * + * @return bool True if the database was selected successfully, otherwise false. + */ function SelectDB($dbName) { // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID); @@ -699,19 +943,32 @@ class ADODB_mysqli extends ADOConnection { if ($this->_connectionID) { $result = @mysqli_select_db($this->_connectionID, $dbName); if (!$result) { - ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg()); + ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->errorMsg()); } return $result; } return false; } - // parameters use PostgreSQL convention, not MySQL + /** + * Executes a provided SQL statement and returns a handle to the result, with the ability to supply a starting + * offset and record count. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:selectlimit + * + * @param string $sql The SQL to execute. + * @param int $nrows (Optional) The limit for the number of records you want returned. By default, all results. + * @param int $offset (Optional) The offset to use when selecting the results. By default, no offset. + * @param array|bool $inputarr (Optional) Any parameter values required by the SQL statement, or false if none. + * @param int $secs (Optional) If greater than 0, perform a cached execute. By default, normal execution. + * + * @return ADORecordSet|false The query results, or false if the query failed to execute. + */ function SelectLimit($sql, - $nrows = -1, - $offset = -1, - $inputarr = false, - $secs = 0) + $nrows = -1, + $offset = -1, + $inputarr = false, + $secs = 0) { $nrows = (int) $nrows; $offset = (int) $offset; @@ -719,30 +976,53 @@ class ADODB_mysqli extends ADOConnection { if ($nrows < 0) $nrows = '18446744073709551615'; if ($secs) - $rs = $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr ); + $rs = $this->cacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr ); else - $rs = $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr ); + $rs = $this->execute($sql . " LIMIT $offsetStr$nrows" , $inputarr ); return $rs; } - + /** + * Prepares an SQL statement and returns a handle to use. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:prepare + * @todo update this function to handle prepared statements correctly + * + * @param string $sql The SQL to prepare. + * + * @return string The original SQL that was provided. + */ function Prepare($sql) { + /* + * Flag the insert_id method to use the correct retrieval method + */ + $this->usePreparedStatement = true; + + /* + * Prepared statements are not yet handled correctly + */ return $sql; $stmt = $this->_connectionID->prepare($sql); if (!$stmt) { - echo $this->ErrorMsg(); + echo $this->errorMsg(); return $sql; } return array($sql,$stmt); } - - // returns queryID or false + /** + * Return the query id. + * + * @param string|array $sql + * @param array $inputarr + * + * @return bool|mysqli_result + */ function _query($sql, $inputarr) { - global $ADODB_COUNTRECS; + global $ADODB_COUNTRECS; // Move to the next recordset, or return false if there is none. In a stored proc // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result // returns false. I think this is because the last "recordset" is actually just the @@ -763,15 +1043,30 @@ class ADODB_mysqli extends ADOConnection { else $a .= 'd'; } + /* + * set prepared statement flags + */ + if ($this->usePreparedStatement) + $this->useLastInsertStatement = true; + $fnarr = array_merge( array($stmt,$a) , $inputarr); - $ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr); + call_user_func_array('mysqli_stmt_bind_param',$fnarr); $ret = mysqli_stmt_execute($stmt); return $ret; } + else + { + /* + * reset prepared statement flags, in case we set them + * previously and didn't use them + */ + $this->usePreparedStatement = false; + $this->useLastInsertStatement = false; + } /* if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) { - if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg()); + if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg()); return false; } @@ -791,13 +1086,19 @@ class ADODB_mysqli extends ADOConnection { } if($this->debug) - ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg()); + ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg()); return false; } - /* Returns: the last error message from previous database operation */ + /** + * Returns a database specific error message. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:errormsg + * + * @return string The last error message. + */ function ErrorMsg() { if (empty($this->_connectionID)) @@ -807,7 +1108,11 @@ class ADODB_mysqli extends ADOConnection { return $this->_errorMsg; } - /* Returns: the last error number from previous database operation */ + /** + * Returns the last error number from previous database operation. + * + * @return int The last error number. + */ function ErrorNo() { if (empty($this->_connectionID)) @@ -816,37 +1121,48 @@ class ADODB_mysqli extends ADOConnection { return @mysqli_errno($this->_connectionID); } - // returns true or false + /** + * Close the database connection. + * + * @return void + */ function _close() { - @mysqli_close($this->_connectionID); + if($this->_connectionID) { + mysqli_close($this->_connectionID); + } $this->_connectionID = false; } - /* - * Maximum size of C field - */ + /** + * Returns the largest length of data that can be inserted into a character field. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:charmax + * + * @return int + */ function CharMax() { return 255; } - /* - * Maximum size of X field - */ + /** + * Returns the largest length of data that can be inserted into a text field. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:textmax + * + * @return int + */ function TextMax() { return 4294967295; } - - // this is a set of functions for managing client encoding - very important if the encodings - // of your database and your output target (i.e. HTML) don't match - // for instance, you may have UTF8 database and server it on-site as latin1 etc. - // GetCharSet - get the name of the character set the client is using now - // Under Windows, the functions should work with MySQL 4.1.11 and above, the set of charsets supported - // depends on compile flags of mysql distribution - + /** + * Get the name of the character set the client connection is using now. + * + * @return string|bool The name of the character set, or false if it can't be determined. + */ function GetCharSet() { //we will use ADO's builtin property charSet @@ -861,7 +1177,15 @@ class ADODB_mysqli extends ADOConnection { } } - // SetCharSet - switch the client encoding + /** + * Sets the character set for database connections (limited databases). + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:setcharset + * + * @param string $charset_name The character set to switch to. + * + * @return bool True if the character set was changed successfully, otherwise false. + */ function SetCharSet($charset_name) { if (!method_exists($this->_connectionID,'set_charset')) { @@ -870,7 +1194,7 @@ class ADODB_mysqli extends ADOConnection { if ($this->charSet !== $charset_name) { $if = @$this->_connectionID->set_charset($charset_name); - return ($if === true & $this->GetCharSet() == $charset_name); + return ($if === true & $this->getCharSet() == $charset_name); } else { return true; } @@ -878,10 +1202,9 @@ class ADODB_mysqli extends ADOConnection { } -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - +/** + * Class ADORecordSet_mysqli + */ class ADORecordSet_mysqli extends ADORecordSet{ var $databaseType = "mysqli"; @@ -939,6 +1262,15 @@ class ADORecordSet_mysqli extends ADORecordSet{ 131072 = MYSQLI_BINCMP_FLAG */ + /** + * Returns raw, database specific information about a field. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:recordset:fetchfield + * + * @param int $fieldOffset (Optional) The field number to get information for. + * + * @return ADOFieldObject|bool + */ function FetchField($fieldOffset = -1) { $fieldnr = $fieldOffset; @@ -960,19 +1292,43 @@ class ADORecordSet_mysqli extends ADORecordSet{ // $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */ $o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG; - return $o; + /* + * Trivial method to cast class to ADOfieldObject + */ + $a = new ADOFieldObject; + foreach (get_object_vars($o) as $key => $name) + $a->$key = $name; + return $a; } + /** + * Reads a row in associative mode if the recordset fetch mode is numeric. + * Using this function when the fetch mode is set to ADODB_FETCH_ASSOC may produce unpredictable results. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:getrowassoc + * + * @param int $upper Indicates whether the keys of the recordset should be upper case or lower case. + * + * @return array|bool + */ function GetRowAssoc($upper = ADODB_ASSOC_CASE) { if ($this->fetchMode == MYSQLI_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) { return $this->fields; } - $row = ADORecordSet::GetRowAssoc($upper); + $row = ADORecordSet::getRowAssoc($upper); return $row; } - /* Use associative array to get fields array */ + /** + * Returns a single field in a single row of the current recordset. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:recordset:fields + * + * @param string $colname The name of the field to retrieve. + * + * @return mixed + */ function Fields($colname) { if ($this->fetchMode != MYSQLI_NUM) { @@ -982,13 +1338,20 @@ class ADORecordSet_mysqli extends ADORecordSet{ if (!$this->bind) { $this->bind = array(); for ($i = 0; $i < $this->_numOfFields; $i++) { - $o = $this->FetchField($i); + $o = $this->fetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } + /** + * Adjusts the result pointer to an arbitrary row in the result. + * + * @param int $row The row to seek to. + * + * @return bool False if the recordset contains no rows, otherwise true. + */ function _seek($row) { if ($this->_numOfRows == 0 || $row < 0) { @@ -1000,10 +1363,16 @@ class ADORecordSet_mysqli extends ADORecordSet{ return true; } - + /** + * In databases that allow accessing of recordsets, retrieves the next set. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:recordset:nextrecordset + * + * @return bool + */ function NextRecordSet() { - global $ADODB_COUNTRECS; + global $ADODB_COUNTRECS; mysqli_free_result($this->_queryID); $this->_queryID = -1; @@ -1011,25 +1380,32 @@ class ADORecordSet_mysqli extends ADORecordSet{ // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result // returns false. I think this is because the last "recordset" is actually just the // return value of the stored proc (ie the number of rows affected). - if(!mysqli_next_result($this->connection->_connectionID)) { - return false; + if (!mysqli_next_result($this->connection->_connectionID)) { + return false; } + // CD: There is no $this->_connectionID variable, at least in the ADO version I'm using - $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->connection->_connectionID ) - : @mysqli_use_result( $this->connection->_connectionID ); - if(!$this->_queryID) { + $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result($this->connection->_connectionID) + : @mysqli_use_result($this->connection->_connectionID); + + if (!$this->_queryID) { return false; } - $this->_inited = false; - $this->bind = false; + + $this->_inited = false; + $this->bind = false; $this->_currentRow = -1; - $this->Init(); + $this->init(); return true; } - // 10% speedup to move MoveNext to child class - // This is the only implementation that works now (23-10-2003). - // Other functions return no or the wrong results. + /** + * Moves the cursor to the next record of the recordset from the current position. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:movenext + * + * @return bool False if there are no more records to move on to, otherwise true. + */ function MoveNext() { if ($this->EOF) return false; @@ -1044,6 +1420,11 @@ class ADORecordSet_mysqli extends ADORecordSet{ return false; } + /** + * Attempt to fetch a result row using the current fetch mode and return whether or not this was successful. + * + * @return bool True if row was fetched successfully, otherwise false. + */ function _fetch() { $this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode); @@ -1051,18 +1432,23 @@ class ADORecordSet_mysqli extends ADORecordSet{ return is_array($this->fields); } + /** + * Frees the memory associated with a result. + * + * @return void + */ function _close() { - //if results are attached to this pointer from Stored Proceedure calls, the next standard query will die 2014 - //only a problem with persistant connections + //if results are attached to this pointer from Stored Procedure calls, the next standard query will die 2014 + //only a problem with persistent connections - if(isset($this->connection->_connectionID) && $this->connection->_connectionID) { - while(mysqli_more_results($this->connection->_connectionID)){ + if (isset($this->connection->_connectionID) && $this->connection->_connectionID) { + while (mysqli_more_results($this->connection->_connectionID)) { mysqli_next_result($this->connection->_connectionID); } } - if($this->_queryID instanceof mysqli_result) { + if ($this->_queryID instanceof mysqli_result) { mysqli_free_result($this->_queryID); } $this->_queryID = false; @@ -1097,6 +1483,15 @@ class ADORecordSet_mysqli extends ADORecordSet{ 255 = MYSQLI_TYPE_GEOMETRY */ + /** + * Get the MetaType character for a given field type. + * + * @param string|object $t The type to get the MetaType character for. + * @param int $len (Optional) Redundant. Will always be set to -1. + * @param bool|object $fieldobj (Optional) + * + * @return string The MetaType + */ function MetaType($t, $len = -1, $fieldobj = false) { if (is_object($t)) { @@ -1105,99 +1500,107 @@ class ADORecordSet_mysqli extends ADORecordSet{ $len = $fieldobj->max_length; } - $len = -1; // mysql max_length is not accurate switch (strtoupper($t)) { - case 'STRING': - case 'CHAR': - case 'VARCHAR': - case 'TINYBLOB': - case 'TINYTEXT': - case 'ENUM': - case 'SET': + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': - case MYSQLI_TYPE_TINY_BLOB : - #case MYSQLI_TYPE_CHAR : - case MYSQLI_TYPE_STRING : - case MYSQLI_TYPE_ENUM : - case MYSQLI_TYPE_SET : - case 253 : - if ($len <= $this->blobSize) return 'C'; + case MYSQLI_TYPE_TINY_BLOB : +// case MYSQLI_TYPE_CHAR : + case MYSQLI_TYPE_STRING : + case MYSQLI_TYPE_ENUM : + case MYSQLI_TYPE_SET : + case 253 : + if ($len <= $this->blobSize) { + return 'C'; + } - case 'TEXT': - case 'LONGTEXT': - case 'MEDIUMTEXT': - return 'X'; + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; - // php_mysql extension always returns 'blob' even if 'text' - // so we have to check whether binary... - case 'IMAGE': - case 'LONGBLOB': - case 'BLOB': - case 'MEDIUMBLOB': + // php_mysql extension always returns 'blob' even if 'text' + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': - case MYSQLI_TYPE_BLOB : - case MYSQLI_TYPE_LONG_BLOB : - case MYSQLI_TYPE_MEDIUM_BLOB : - return !empty($fieldobj->binary) ? 'B' : 'X'; + case MYSQLI_TYPE_BLOB : + case MYSQLI_TYPE_LONG_BLOB : + case MYSQLI_TYPE_MEDIUM_BLOB : + return !empty($fieldobj->binary) ? 'B' : 'X'; - case 'YEAR': - case 'DATE': - case MYSQLI_TYPE_DATE : - case MYSQLI_TYPE_YEAR : - return 'D'; + case 'YEAR': + case 'DATE': + case MYSQLI_TYPE_DATE : + case MYSQLI_TYPE_YEAR : + return 'D'; - case 'TIME': - case 'DATETIME': - case 'TIMESTAMP': + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': - case MYSQLI_TYPE_DATETIME : - case MYSQLI_TYPE_NEWDATE : - case MYSQLI_TYPE_TIME : - case MYSQLI_TYPE_TIMESTAMP : - return 'T'; + case MYSQLI_TYPE_DATETIME : + case MYSQLI_TYPE_NEWDATE : + case MYSQLI_TYPE_TIME : + case MYSQLI_TYPE_TIMESTAMP : + return 'T'; - case 'INT': - case 'INTEGER': - case 'BIGINT': - case 'TINYINT': - case 'MEDIUMINT': - case 'SMALLINT': + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': - case MYSQLI_TYPE_INT24 : - case MYSQLI_TYPE_LONG : - case MYSQLI_TYPE_LONGLONG : - case MYSQLI_TYPE_SHORT : - case MYSQLI_TYPE_TINY : - if (!empty($fieldobj->primary_key)) return 'R'; - return 'I'; + case MYSQLI_TYPE_INT24 : + case MYSQLI_TYPE_LONG : + case MYSQLI_TYPE_LONGLONG : + case MYSQLI_TYPE_SHORT : + case MYSQLI_TYPE_TINY : + if (!empty($fieldobj->primary_key)) { + return 'R'; + } + return 'I'; - // Added floating-point types - // Maybe not necessery. - case 'FLOAT': - case 'DOUBLE': -// case 'DOUBLE PRECISION': - case 'DECIMAL': - case 'DEC': - case 'FIXED': - default: - //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>"; - return 'N'; + // Added floating-point types + // Maybe not necessary. + case 'FLOAT': + case 'DOUBLE': +// case 'DOUBLE PRECISION': + case 'DECIMAL': + case 'DEC': + case 'FIXED': + default: + //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>"; + return 'N'; } - } // function + } } // rs class -} - -class ADORecordSet_array_mysqli extends ADORecordSet_array { - - function __construct($id=-1,$mode=false) - { - parent::__construct($id,$mode); - } - +/** + * Class ADORecordSet_array_mysqli + */ +class ADORecordSet_array_mysqli extends ADORecordSet_array +{ + /** + * Get the MetaType character for a given field type. + * + * @param string|object $t The type to get the MetaType character for. + * @param int $len (Optional) Redundant. Will always be set to -1. + * @param bool|object $fieldobj (Optional) + * + * @return string The MetaType + */ function MetaType($t, $len = -1, $fieldobj = false) { if (is_object($t)) { @@ -1206,90 +1609,89 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array { $len = $fieldobj->max_length; } - $len = -1; // mysql max_length is not accurate switch (strtoupper($t)) { - case 'STRING': - case 'CHAR': - case 'VARCHAR': - case 'TINYBLOB': - case 'TINYTEXT': - case 'ENUM': - case 'SET': - - case MYSQLI_TYPE_TINY_BLOB : - #case MYSQLI_TYPE_CHAR : - case MYSQLI_TYPE_STRING : - case MYSQLI_TYPE_ENUM : - case MYSQLI_TYPE_SET : - case 253 : - if ($len <= $this->blobSize) return 'C'; - - case 'TEXT': - case 'LONGTEXT': - case 'MEDIUMTEXT': - return 'X'; - - // php_mysql extension always returns 'blob' even if 'text' - // so we have to check whether binary... - case 'IMAGE': - case 'LONGBLOB': - case 'BLOB': - case 'MEDIUMBLOB': + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': - case MYSQLI_TYPE_BLOB : - case MYSQLI_TYPE_LONG_BLOB : - case MYSQLI_TYPE_MEDIUM_BLOB : - - return !empty($fieldobj->binary) ? 'B' : 'X'; - case 'YEAR': - case 'DATE': - case MYSQLI_TYPE_DATE : - case MYSQLI_TYPE_YEAR : - - return 'D'; + case MYSQLI_TYPE_TINY_BLOB : +// case MYSQLI_TYPE_CHAR : + case MYSQLI_TYPE_STRING : + case MYSQLI_TYPE_ENUM : + case MYSQLI_TYPE_SET : + case 253 : + if ($len <= $this->blobSize) { + return 'C'; + } - case 'TIME': - case 'DATETIME': - case 'TIMESTAMP': + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; - case MYSQLI_TYPE_DATETIME : - case MYSQLI_TYPE_NEWDATE : - case MYSQLI_TYPE_TIME : - case MYSQLI_TYPE_TIMESTAMP : + // php_mysql extension always returns 'blob' even if 'text' + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': - return 'T'; + case MYSQLI_TYPE_BLOB : + case MYSQLI_TYPE_LONG_BLOB : + case MYSQLI_TYPE_MEDIUM_BLOB : + return !empty($fieldobj->binary) ? 'B' : 'X'; - case 'INT': - case 'INTEGER': - case 'BIGINT': - case 'TINYINT': - case 'MEDIUMINT': - case 'SMALLINT': + case 'YEAR': + case 'DATE': + case MYSQLI_TYPE_DATE : + case MYSQLI_TYPE_YEAR : + return 'D'; - case MYSQLI_TYPE_INT24 : - case MYSQLI_TYPE_LONG : - case MYSQLI_TYPE_LONGLONG : - case MYSQLI_TYPE_SHORT : - case MYSQLI_TYPE_TINY : + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': - if (!empty($fieldobj->primary_key)) return 'R'; + case MYSQLI_TYPE_DATETIME : + case MYSQLI_TYPE_NEWDATE : + case MYSQLI_TYPE_TIME : + case MYSQLI_TYPE_TIMESTAMP : + return 'T'; - return 'I'; + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': + case MYSQLI_TYPE_INT24 : + case MYSQLI_TYPE_LONG : + case MYSQLI_TYPE_LONGLONG : + case MYSQLI_TYPE_SHORT : + case MYSQLI_TYPE_TINY : + if (!empty($fieldobj->primary_key)) { + return 'R'; + } + return 'I'; - // Added floating-point types - // Maybe not necessery. - case 'FLOAT': - case 'DOUBLE': -// case 'DOUBLE PRECISION': - case 'DECIMAL': - case 'DEC': - case 'FIXED': - default: - //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>"; - return 'N'; + // Added floating-point types + // Maybe not necessary. + case 'FLOAT': + case 'DOUBLE': +// case 'DOUBLE PRECISION': + case 'DECIMAL': + case 'DEC': + case 'FIXED': + default: + //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>"; + return 'N'; } - } // function - + } } + +} // if defined _ADODB_MYSQLI_LAYER diff --git a/drivers/adodb-mysqlpo.inc.php b/drivers/adodb-mysqlpo.inc.php index 823f86ad..cf430799 100644 --- a/drivers/adodb-mysqlpo.inc.php +++ b/drivers/adodb-mysqlpo.inc.php @@ -11,8 +11,8 @@ MySQL code that supports transactions. For MySQL 3.23 or later. Code from James Poon <jpoon88@yahoo.com> - - This driver extends the deprecated mysql driver, and was originally designed to be a + + This driver extends the deprecated mysql driver, and was originally designed to be a portable driver in the same manner as oci8po and mssqlpo. Its functionality is exactly duplicated in the mysqlt driver, which is itself deprecated. This driver will be removed in ADOdb version 6.0.0. @@ -32,11 +32,6 @@ class ADODB_mysqlt extends ADODB_mysql { var $hasTransactions = true; var $autoRollback = true; // apparently mysql does not autorollback properly - function __construct() - { - global $ADODB_EXTENSION; if ($ADODB_EXTENSION) $this->rsPrefix .= 'ext_'; - } - function BeginTrans() { if ($this->transOff) return true; @@ -116,11 +111,6 @@ class ADORecordSet_mysqlt extends ADORecordSet_mysql{ class ADORecordSet_ext_mysqlt extends ADORecordSet_mysqlt { - function __construct($queryID,$mode=false) - { - parent::__construct($queryID,$mode); - } - function MoveNext() { return adodb_movenext($this); diff --git a/drivers/adodb-mysqlt.inc.php b/drivers/adodb-mysqlt.inc.php index cc824e7d..d912db59 100644 --- a/drivers/adodb-mysqlt.inc.php +++ b/drivers/adodb-mysqlt.inc.php @@ -10,7 +10,7 @@ Set tabs to 8. This driver only supports the original MySQL driver in transactional mode. It - is deprected in PHP version 5.5 and removed in PHP version 7. It is deprecated + is deprecated in PHP version 5.5 and removed in PHP version 7. It is deprecated as of ADOdb version 5.20.0. Use the mysqli driver instead, which supports both transactional and non-transactional updates @@ -29,11 +29,6 @@ class ADODB_mysqlt extends ADODB_mysql { var $hasTransactions = true; var $autoRollback = true; // apparently mysql does not autorollback properly - function __construct() - { - global $ADODB_EXTENSION; if ($ADODB_EXTENSION) $this->rsPrefix .= 'ext_'; - } - /* set transaction mode SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL diff --git a/drivers/adodb-netezza.inc.php b/drivers/adodb-netezza.inc.php index b25705f7..3b02b904 100644 --- a/drivers/adodb-netezza.inc.php +++ b/drivers/adodb-netezza.inc.php @@ -14,7 +14,7 @@ Fixed the way data types and lengths are returned in MetaColumns() as well as added the default lengths for certain types Updated public variables for Netezza - Still need to remove blob functions, as Netezza doesn't suppport blob + Still need to remove blob functions, as Netezza doesn't support blob */ // security - hide paths if (!defined('ADODB_DIR')) die(); @@ -50,11 +50,6 @@ class ADODB_netezza extends ADODB_postgres64 { // http://bugs.php.net/bug.php?id=25404 - function __construct() - { - - } - function MetaColumns($table,$upper=true) { @@ -141,11 +136,6 @@ class ADORecordSet_netezza extends ADORecordSet_postgres64 var $databaseType = "netezza"; var $canSeek = true; - function __construct($queryID,$mode=false) - { - parent::__construct($queryID,$mode); - } - // _initrs modified to disable blob handling function _initrs() { diff --git a/drivers/adodb-oci8.inc.php b/drivers/adodb-oci8.inc.php index 3e45edc7..1774d87c 100644 --- a/drivers/adodb-oci8.inc.php +++ b/drivers/adodb-oci8.inc.php @@ -89,7 +89,6 @@ END; var $connectSID = false; var $_bind = false; var $_nestedSQL = true; - var $_hasOciFetchStatement = false; var $_getarray = false; // currently not working var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER var $session_sharing_force_blob = false; // alter session on updateblob if set to true @@ -103,13 +102,20 @@ END; // var $ansiOuter = true; // if oracle9 - function __construct() - { - $this->_hasOciFetchStatement = ADODB_PHPVER >= 0x4200; - if (defined('ADODB_EXTENSION')) { - $this->rsPrefix .= 'ext_'; - } - } + /* + * Legacy compatibility for sequence names for emulated auto-increments + */ + public $useCompactAutoIncrements = false; + + /* + * Defines the schema name for emulated auto-increment columns + */ + public $schema = false; + + /* + * Defines the prefix for emulated auto-increment columns + */ + public $seqPrefix = 'SEQ_'; /* function MetaColumns($table, $normalize=true) added by smondino@users.sourceforge.net*/ function MetaColumns($table, $normalize=true) @@ -307,6 +313,43 @@ END; { return " NVL($field, $ifNull) "; // if Oracle } + + function _insertid($tabname,$column='') + { + + if (!$this->seqField) + return false; + + + if ($this->schema) + { + $t = strpos($tabname,'.'); + if ($t !== false) + $tab = substr($tabname,$t+1); + else + $tab = $tabname; + + if ($this->useCompactAutoIncrements) + $tab = sprintf('%u',crc32(strtolower($tab))); + + $seqname = $this->schema.'.'.$this->seqPrefix.$tab; + } + else + { + if ($this->useCompactAutoIncrements) + $tabname = sprintf('%u',crc32(strtolower($tabname))); + + $seqname = $this->seqPrefix.$tabname; + } + + if (strlen($seqname) > 30) + /* + * We cannot successfully identify the sequence + */ + return false; + + return $this->getOne("SELECT $seqname.currval FROM dual"); + } // format and return date string in database date format function DBDate($d,$isfld=false) @@ -1751,7 +1794,8 @@ class ADORecordset_oci8 extends ADORecordSet { oci_free_cursor($this->_refcursor); $this->_refcursor = false; } - @oci_free_statement($this->_queryID); + if (is_resource($this->_queryID)) + @oci_free_statement($this->_queryID); $this->_queryID = false; } @@ -1808,16 +1852,12 @@ class ADORecordset_oci8 extends ADORecordSet { return 'I'; default: - return 'N'; + return ADODB_DEFAULT_METATYPE; } } } class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 { - function __construct($queryID,$mode=false) - { - parent::__construct($queryID, $mode); - } function MoveNext() { diff --git a/drivers/adodb-oci805.inc.php b/drivers/adodb-oci805.inc.php index 18222195..0ebcf6a1 100644 --- a/drivers/adodb-oci805.inc.php +++ b/drivers/adodb-oci805.inc.php @@ -48,8 +48,4 @@ class ADODB_oci805 extends ADODB_oci8 { class ADORecordset_oci805 extends ADORecordset_oci8 { var $databaseType = "oci805"; - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-oci8po.inc.php b/drivers/adodb-oci8po.inc.php index 1ad16446..acfa3d27 100644 --- a/drivers/adodb-oci8po.inc.php +++ b/drivers/adodb-oci8po.inc.php @@ -30,12 +30,6 @@ class ADODB_oci8po extends ADODB_oci8 { var $metaColumnsSQL = "select lower(cname),coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net var $metaTablesSQL = "select lower(table_name),table_type from cat where table_type in ('TABLE','VIEW')"; - function __construct() - { - $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200; - # oci8po does not support adodb extension: adodb_movenext() - } - function Param($name,$type='C') { return '?'; @@ -86,6 +80,7 @@ class ADODB_oci8po extends ADODB_oci8 { } return ADODB_oci8::_query($sql,$inputarr); } + /** * Replaces compatibility bind markers with oracle ones and returns a * valid sql statement @@ -94,7 +89,7 @@ class ADODB_oci8po extends ADODB_oci8 { * to numerous tweaks, as more extreme test cases have appeared. This * is now done this like this to help maintainability and avoid the * need to rely on regexp experienced maintainers - * + * * @param string $sql The sql statement * @param string[] $inputarr The bind array * @@ -120,7 +115,7 @@ class ADODB_oci8po extends ADODB_oci8 { /* * find the next character of the string */ - $c = $sql{$i}; + $c = $sql[$i]; if ($c == "'" && !$inString && $escaped==0) /* @@ -173,11 +168,6 @@ class ADORecordset_oci8po extends ADORecordset_oci8 { var $databaseType = 'oci8po'; - function __construct($queryID,$mode=false) - { - parent::__construct($queryID,$mode); - } - function Fields($colname) { if ($this->fetchMode & OCI_ASSOC) return $this->fields[$colname]; diff --git a/drivers/adodb-oci8quercus.inc.php b/drivers/adodb-oci8quercus.inc.php index 19eab03e..a226c290 100644 --- a/drivers/adodb-oci8quercus.inc.php +++ b/drivers/adodb-oci8quercus.inc.php @@ -28,10 +28,6 @@ class ADODB_oci8quercus extends ADODB_oci8 { var $databaseType = 'oci8quercus'; var $dataProvider = 'oci8'; - function __construct() - { - } - } /*-------------------------------------------------------------------------------------- @@ -42,11 +38,6 @@ class ADORecordset_oci8quercus extends ADORecordset_oci8 { var $databaseType = 'oci8quercus'; - function __construct($queryID,$mode=false) - { - parent::__construct($queryID,$mode); - } - function _FetchField($fieldOffset = -1) { global $QUERCUS; diff --git a/drivers/adodb-odbc.inc.php b/drivers/adodb-odbc.inc.php index d38894e8..bf87faed 100644 --- a/drivers/adodb-odbc.inc.php +++ b/drivers/adodb-odbc.inc.php @@ -17,6 +17,18 @@ if (!defined('ADODB_DIR')) die(); define("_ADODB_ODBC_LAYER", 2 ); +/* + * These constants are used to set define MetaColumns() method's behavior. + * - METACOLUMNS_RETURNS_ACTUAL makes the driver return the actual type, + * like all other drivers do (default) + * - METACOLUMNS_RETURNS_META is provided for legacy compatibility (makes + * driver behave as it did prior to v5.21) + * + * @see $metaColumnsReturnType + */ +DEFINE('METACOLUMNS_RETURNS_ACTUAL', 0); +DEFINE('METACOLUMNS_RETURNS_META', 1); + /*-------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------*/ @@ -36,16 +48,15 @@ class ADODB_odbc extends ADOConnection { var $curmode = SQL_CUR_USE_DRIVER; // See sqlext.h, SQL_CUR_DEFAULT == SQL_CUR_USE_DRIVER == 2L var $_genSeqSQL = "create table %s (id integer)"; var $_autocommit = true; - var $_haserrorfunctions = true; - var $_has_stupid_odbc_fetch_api_change = true; var $_lastAffectedRows = 0; var $uCaseTables = true; // for meta* functions, uppercase table names + + /* + * Tells the metaColumns feature whether to return actual or meta type + */ + public $metaColumnsReturnType = METACOLUMNS_RETURNS_ACTUAL; - function __construct() - { - $this->_haserrorfunctions = ADODB_PHPVER >= 0x4050; - $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; - } + function __construct() {} // returns true or false function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) @@ -93,7 +104,7 @@ class ADODB_odbc extends ADOConnection { function ServerInfo() { - if (!empty($this->host) && ADODB_PHPVER >= 0x4300) { + if (!empty($this->host)) { $dsn = strtoupper($this->host); $first = true; $found = false; @@ -180,30 +191,25 @@ class ADODB_odbc extends ADOConnection { function ErrorMsg() { - if ($this->_haserrorfunctions) { - if ($this->_errorMsg !== false) return $this->_errorMsg; - if (empty($this->_connectionID)) return @odbc_errormsg(); - return @odbc_errormsg($this->_connectionID); - } else return ADOConnection::ErrorMsg(); + if ($this->_errorMsg !== false) return $this->_errorMsg; + if (empty($this->_connectionID)) return @odbc_errormsg(); + return @odbc_errormsg($this->_connectionID); } function ErrorNo() { + if ($this->_errorCode !== false) { + // bug in 4.0.6, error number can be corrupted string (should be 6 digits) + return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode; + } - if ($this->_haserrorfunctions) { - if ($this->_errorCode !== false) { - // bug in 4.0.6, error number can be corrupted string (should be 6 digits) - return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode; - } - - if (empty($this->_connectionID)) $e = @odbc_error(); - else $e = @odbc_error($this->_connectionID); + if (empty($this->_connectionID)) $e = @odbc_error(); + else $e = @odbc_error($this->_connectionID); - // bug in 4.0.6, error number can be corrupted string (should be 6 digits) - // so we check and patch - if (strlen($e)<=2) return 0; - return $e; - } else return ADOConnection::ErrorNo(); + // bug in 4.0.6, error number can be corrupted string (should be 6 digits) + // so we check and patch + if (strlen($e)<=2) return 0; + return $e; } @@ -258,7 +264,6 @@ class ADODB_odbc extends ADOConnection { $ADODB_FETCH_MODE = $savem; if (!$rs) return false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $arr = $rs->GetArray(); $rs->Close(); @@ -287,7 +292,6 @@ class ADODB_odbc extends ADOConnection { $false = false; return $false; } - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $arr = $rs->GetArray(); //print_r($arr); @@ -390,12 +394,11 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/od $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - /*if (false) { // after testing, confirmed that the following does not work becoz of a bug + /*if (false) { // after testing, confirmed that the following does not work because of a bug $qid2 = odbc_tables($this->_connectionID); $rs = new ADORecordSet_odbc($qid2); $ADODB_FETCH_MODE = $savem; if (!$rs) return false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $rs->_fetch(); while (!$rs->EOF) { @@ -434,7 +437,6 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/od $ADODB_FETCH_MODE = $savem; if (!$rs) return $false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $rs->_fetch(); $retarr = array(); @@ -459,7 +461,16 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/od if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[3]; - $fld->type = $this->ODBCTypes($rs->fields[4]); + if ($this->metaColumnsReturnType == METACOLUMNS_RETURNS_META) + /* + * This is the broken, original value + */ + $fld->type = $this->ODBCTypes($rs->fields[4]); + else + /* + * This is the correct new value + */ + $fld->type = $rs->fields[4]; // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp // access uses precision to store length for char/varchar @@ -516,10 +527,8 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/od if (! odbc_execute($stmtid,$inputarr)) { //@odbc_free_result($stmtid); - if ($this->_haserrorfunctions) { - $this->_errorMsg = odbc_errormsg(); - $this->_errorCode = odbc_error(); - } + $this->_errorMsg = odbc_errormsg(); + $this->_errorCode = odbc_error(); return false; } @@ -527,10 +536,8 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/od $stmtid = $sql[1]; if (!odbc_execute($stmtid)) { //@odbc_free_result($stmtid); - if ($this->_haserrorfunctions) { - $this->_errorMsg = odbc_errormsg(); - $this->_errorCode = odbc_error(); - } + $this->_errorMsg = odbc_errormsg(); + $this->_errorCode = odbc_error(); return false; } } else @@ -547,19 +554,11 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/od odbc_longreadlen($stmtid,$this->maxblobsize); } - if ($this->_haserrorfunctions) { - $this->_errorMsg = ''; - $this->_errorCode = 0; - } else { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - } + $this->_errorMsg = ''; + $this->_errorCode = 0; } else { - if ($this->_haserrorfunctions) { - $this->_errorMsg = odbc_errormsg(); - $this->_errorCode = odbc_error(); - } else { - $this->_errorMsg = $this->getChangedErrorMsg($last_php_error); - } + $this->_errorMsg = odbc_errormsg(); + $this->_errorCode = odbc_error(); } return $stmtid; } @@ -603,7 +602,6 @@ class ADORecordSet_odbc extends ADORecordSet { var $databaseType = "odbc"; var $dataProvider = "odbc"; var $useFetchArray; - var $_has_stupid_odbc_fetch_api_change; function __construct($id,$mode=false) { @@ -661,7 +659,6 @@ class ADORecordSet_odbc extends ADORecordSet { // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 if ($this->_numOfRows == 0) $this->_numOfRows = -1; //$this->useFetchArray = $this->connection->useFetchArray; - $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; } function _seek($row) @@ -712,12 +709,7 @@ class ADORecordSet_odbc extends ADORecordSet { function _fetch() { $this->fields = false; - if ($this->_has_stupid_odbc_fetch_api_change) - $rez = @odbc_fetch_into($this->_queryID,$this->fields); - else { - $row = 0; - $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields); - } + $rez = @odbc_fetch_into($this->_queryID,$this->fields); if ($rez) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields = $this->GetRowAssoc(); diff --git a/drivers/adodb-odbc_db2.inc.php b/drivers/adodb-odbc_db2.inc.php index de4f0ec5..4c88c1c3 100644 --- a/drivers/adodb-odbc_db2.inc.php +++ b/drivers/adodb-odbc_db2.inc.php @@ -28,7 +28,7 @@ to SQL_CUR_USE_ODBC Cursor Type, then the whole query speed up from 1 till 10 seconds to 0.2 till 0.3 seconds for 100 records. Amazing!!! -Therfore, PHP is just almost fast as calling the DB2 +Therefore, PHP is just almost fast as calling the DB2 from Servlets using JDBC (don't take too much care about the speed at whole: the database was on a completely other location, so the whole connection @@ -92,7 +92,7 @@ to DB2 full rights to the DB2 SQLLIB directory, and place the user in the DBUSER if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } if (!defined('ADODB_ODBC_DB2')){ define('ADODB_ODBC_DB2',1); @@ -157,7 +157,6 @@ class ADODB_ODBC_DB2 extends ADODB_odbc { $false = false; return $false; } - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $arr = $rs->GetArray(); //print_r($arr); @@ -306,11 +305,6 @@ class ADORecordSet_odbc_db2 extends ADORecordSet_odbc { var $databaseType = "db2"; - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } - function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { @@ -361,7 +355,7 @@ class ADORecordSet_odbc_db2 extends ADORecordSet_odbc { case 'I': return 'I'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } } diff --git a/drivers/adodb-odbc_mssql.inc.php b/drivers/adodb-odbc_mssql.inc.php index 6856de9f..8accfeed 100644 --- a/drivers/adodb-odbc_mssql.inc.php +++ b/drivers/adodb-odbc_mssql.inc.php @@ -18,7 +18,7 @@ Set tabs to 4 for best viewing. if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } @@ -47,12 +47,6 @@ class ADODB_odbc_mssql extends ADODB_odbc { var $connectStmt = 'SET CONCAT_NULL_YIELDS_NULL OFF'; # When SET CONCAT_NULL_YIELDS_NULL is ON, # concatenating a null value with a string yields a NULL result - function __construct() - { - parent::__construct(); - //$this->curmode = SQL_CUR_USE_ODBC; - } - // crashes php... function ServerInfo() { @@ -351,6 +345,85 @@ order by constraint_name, referenced_table_name, keyno"; } return $s; } + + /** + * Returns a substring of a varchar type field + * + * The SQL server version varies because the length is mandatory, so + * we append a reasonable string length + * + * @param string $fld The field to sub-string + * @param int $start The start point + * @param int $length An optional length + * + * @return The SQL text + */ + function substr($fld,$start,$length=0) + { + if ($length == 0) + /* + * The length available to varchar is 2GB, but that makes no + * sense in a substring, so I'm going to arbitrarily limit + * the length to 1K, but you could change it if you want + */ + $length = 1024; + + $text = "SUBSTRING($fld,$start,$length)"; + return $text; + } + + /** + * Returns the maximum size of a MetaType C field. Because of the + * database design, SQL Server places no limits on the size of data inserted + * Although the actual limit is 2^31-1 bytes. + * + * @return int + */ + function charMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + + /** + * Returns the maximum size of a MetaType X field. Because of the + * database design, SQL Server places no limits on the size of data inserted + * Although the actual limit is 2^31-1 bytes. + * + * @return int + */ + function textMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + + // returns concatenated string + // MSSQL requires integers to be cast as strings + // automatically cast every datatype to VARCHAR(255) + // @author David Rogers (introspectshun) + function Concat() + { + $s = ""; + $arr = func_get_args(); + + // Split single record on commas, if possible + if (sizeof($arr) == 1) { + foreach ($arr as $arg) { + $args = explode(',', $arg); + } + $arr = $args; + } + + array_walk( + $arr, + function(&$value, $key) { + $value = "CAST(" . $value . " AS VARCHAR(255))"; + } + ); + $s = implode('+',$arr); + if (sizeof($arr) > 0) return "$s"; + + return ''; + } } @@ -358,8 +431,4 @@ class ADORecordSet_odbc_mssql extends ADORecordSet_odbc { var $databaseType = 'odbc_mssql'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-odbc_mssql2012.inc.php b/drivers/adodb-odbc_mssql2012.inc.php new file mode 100644 index 00000000..a3b7f69f --- /dev/null +++ b/drivers/adodb-odbc_mssql2012.inc.php @@ -0,0 +1,28 @@ +<?php +/* + @version v5.21.0-dev ??-???-2016 + @copyright (c) 2015 Damien Regad, Mark Newnham and the ADOdb community + Released under both BSD license and Lesser GPL library license. + Whenever there is any discrepancy between the two licenses, + the BSD license will take precedence. + Set tabs to 4. + + Microsoft SQL Server 2012 via ODBC +*/ + +if (!defined('ADODB_DIR')) + die(); + +include_once(ADODB_DIR."/drivers/adodb-odbc_mssql.inc.php"); + +class ADODB_odbc_mssql2012 extends ADODB_odbc_mssql +{ + /* + * Makes behavior similar to prior versions of SQL Server + */ + var $connectStmt = 'SET CONCAT_NULL_YIELDS_NULL ON'; +} + +class ADORecordSet_odbc_mssql2012 extends ADORecordSet_odbc_mssql +{ +}
\ No newline at end of file diff --git a/drivers/adodb-odbc_oracle.inc.php b/drivers/adodb-odbc_oracle.inc.php index f4ecdc6d..df621513 100644 --- a/drivers/adodb-odbc_oracle.inc.php +++ b/drivers/adodb-odbc_oracle.inc.php @@ -16,7 +16,7 @@ Set tabs to 4 for best viewing. if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } @@ -101,8 +101,4 @@ class ADORecordSet_odbc_oracle extends ADORecordSet_odbc { var $databaseType = 'odbc_oracle'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-odbtp.inc.php b/drivers/adodb-odbtp.inc.php index a239bae9..aaefaf65 100644 --- a/drivers/adodb-odbtp.inc.php +++ b/drivers/adodb-odbtp.inc.php @@ -35,10 +35,6 @@ class ADODB_odbtp extends ADOConnection{ var $_canPrepareSP = false; var $_dontPoolDBC = true; - function __construct() - { - } - function ServerInfo() { return array('description' => @odbtp_get_attr( ODB_ATTR_DBMSNAME, $this->_connectionID), @@ -792,48 +788,28 @@ class ADORecordSet_odbtp_mssql extends ADORecordSet_odbtp { var $databaseType = 'odbtp_mssql'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } class ADORecordSet_odbtp_access extends ADORecordSet_odbtp { var $databaseType = 'odbtp_access'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } class ADORecordSet_odbtp_vfp extends ADORecordSet_odbtp { var $databaseType = 'odbtp_vfp'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } class ADORecordSet_odbtp_oci8 extends ADORecordSet_odbtp { var $databaseType = 'odbtp_oci8'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } class ADORecordSet_odbtp_sybase extends ADORecordSet_odbtp { var $databaseType = 'odbtp_sybase'; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } } diff --git a/drivers/adodb-odbtp_unicode.inc.php b/drivers/adodb-odbtp_unicode.inc.php index 2f8da4ff..7d33bde4 100644 --- a/drivers/adodb-odbtp_unicode.inc.php +++ b/drivers/adodb-odbtp_unicode.inc.php @@ -26,7 +26,7 @@ if (!defined('ADODB_DIR')) die(); */ if (!defined('_ADODB_ODBTP_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbtp.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbtp.inc.php"); } class ADODB_odbtp_unicode extends ADODB_odbtp { diff --git a/drivers/adodb-oracle.inc.php b/drivers/adodb-oracle.inc.php index b1dfbdf8..89e9cca9 100644 --- a/drivers/adodb-oracle.inc.php +++ b/drivers/adodb-oracle.inc.php @@ -29,10 +29,6 @@ class ADODB_oracle extends ADOConnection { var $sysTimeStamp = 'SYSDATE'; var $connectSID = true; - function __construct() - { - } - // format and return date string in database date format function DBDate($d, $isfld = false) { diff --git a/drivers/adodb-pdo.inc.php b/drivers/adodb-pdo.inc.php index 0142bf1b..89ca0451 100644 --- a/drivers/adodb-pdo.inc.php +++ b/drivers/adodb-pdo.inc.php @@ -77,7 +77,6 @@ class ADODB_pdo extends ADOConnection { var $_genSeqSQL = "create table %s (id integer)"; var $_dropSeqSQL; var $_autocommit = true; - var $_haserrorfunctions = true; var $_lastAffectedRows = 0; var $_errormsg = false; @@ -87,10 +86,6 @@ class ADODB_pdo extends ADOConnection { var $stmt = false; var $_driver; - function __construct() - { - } - function _UpdatePDO() { $d = $this->_driver; @@ -102,6 +97,7 @@ class ADODB_pdo extends ADOConnection { $this->random = $d->random; $this->concat_operator = $d->concat_operator; $this->nameQuote = $d->nameQuote; + $this->arrayClass = $d->arrayClass; $this->hasGenID = $d->hasGenID; $this->_genIDSQL = $d->_genIDSQL; @@ -144,6 +140,7 @@ class ADODB_pdo extends ADOConnection { case 'oci': case 'pgsql': case 'sqlite': + case 'firebird': default: $argDSN .= ';dbname='.$argDatabasename; } @@ -175,6 +172,16 @@ class ADODB_pdo extends ADOConnection { //$this->_connectionID->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT ); $this->_connectionID->setAttribute(PDO::ATTR_CASE,$m); + // Now merge in any provided attributes for PDO + foreach ($this->connectionParameters as $options) { + foreach($options as $k=>$v) { + if ($this->debug) { + ADOconnection::outp('Setting attribute: ' . $k . ' to ' . $v); + } + $this->_connectionID->setAttribute($k,$v); + } + } + $class = 'ADODB_pdo_'.$this->dsnType; //$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true); switch($this->dsnType) { @@ -184,6 +191,8 @@ class ADODB_pdo extends ADOConnection { case 'pgsql': case 'sqlite': case 'sqlsrv': + case 'firebird': + case 'dblib': include_once(ADODB_DIR.'/drivers/adodb-pdo_'.$this->dsnType.'.inc.php'); break; } @@ -210,10 +219,7 @@ class ADODB_pdo extends ADOConnection { return call_user_func_array(array($this->_driver, 'Concat'), $args); } - if (PHP_VERSION >= 5.3) { - return call_user_func_array('parent::Concat', $args); - } - return call_user_func_array(array($this,'parent::Concat'), $args); + return call_user_func_array('parent::Concat', $args); } // returns true or false @@ -251,6 +257,64 @@ class ADODB_pdo extends ADOConnection { return $this->_driver->MetaColumns($table,$normalize); } + public function metaIndexes($table,$normalize=true) + { + if (method_exists($this->_driver,'metaIndexes')) + return $this->_driver->metaIndexes($table,$normalize); + } + + /** + * Return a list of Primary Keys for a specified table. + * + * @param string $table + * @param bool $owner (optional) not used in this driver + * + * @return string[] Array of indexes + */ + public function metaPrimaryKeys($table,$owner=false) + { + if (method_exists($this->_driver,'metaPrimaryKeys')) + return $this->_driver->metaPrimaryKeys($table,$owner); + } + + /** + * Returns a list of Foreign Keys for a specified table. + * + * @param string $table + * @param bool $owner (optional) not used in this driver + * @param bool $upper + * @param bool $associative + * + * @return string[] where keys are tables, and values are foreign keys + */ + public function metaForeignKeys($table, $owner=false, $upper=false,$associative=false) { + if (method_exists($this->_driver,'metaForeignKeys')) + return $this->_driver->metaForeignKeys($table,$owner,$upper,$associative); + } + + /** + * List procedures or functions in an array. + * + * @param $procedureNamePattern A procedure name pattern; must match the procedure name as it is stored in the database. + * @param $catalog A catalog name; must match the catalog name as it is stored in the database. + * @param $schemaPattern A schema name pattern. + * + * @return false|array false if not supported, or array of procedures on current database with structure below + * Array( + * [name_of_procedure] => Array( + * [type] => PROCEDURE or FUNCTION + * [catalog] => Catalog_name + * [schema] => Schema_name + * [remarks] => explanatory comment on the procedure + * ) + * ) + */ + public function metaProcedures($procedureNamePattern = null, $catalog = null, $schemaPattern = null) { + if (method_exists($this->_driver,'metaProcedures')) + return $this->_driver->metaProcedures($procedureNamePattern,$catalog,$schemaPattern); + return false; + } + function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) { $obj = $stmt[1]; @@ -335,16 +399,16 @@ class ADODB_pdo extends ADOConnection { return $err; } - /** - * @param bool $auto_commit - * @return void - */ + /** + * @param bool $auto_commit + * @return void + */ function SetAutoCommit($auto_commit) - { - if(method_exists($this->_driver, 'SetAutoCommit')) { - $this->_driver->SetAutoCommit($auto_commit); - } - } + { + if(method_exists($this->_driver, 'SetAutoCommit')) { + $this->_driver->SetAutoCommit($auto_commit); + } + } function SetTransactionMode($transaction_mode) { @@ -355,10 +419,10 @@ class ADODB_pdo extends ADOConnection { return parent::SetTransactionMode($seqname); } - function BeginTrans() + function beginTrans() { - if(method_exists($this->_driver, 'BeginTrans')) { - return $this->_driver->BeginTrans(); + if(method_exists($this->_driver, 'beginTrans')) { + return $this->_driver->beginTrans(); } if (!$this->hasTransactions) { @@ -374,10 +438,11 @@ class ADODB_pdo extends ADOConnection { return $this->_connectionID->beginTransaction(); } - function CommitTrans($ok=true) + function commitTrans($ok=true) { - if(method_exists($this->_driver, 'CommitTrans')) { - return $this->_driver->CommitTrans($ok); + + if(method_exists($this->_driver, 'commitTrans')) { + return $this->_driver->commitTrans($ok); } if (!$this->hasTransactions) { @@ -387,7 +452,7 @@ class ADODB_pdo extends ADOConnection { return true; } if (!$ok) { - return $this->RollbackTrans(); + return $this->rollbackTrans(); } if ($this->transCnt) { $this->transCnt -= 1; @@ -441,10 +506,10 @@ class ADODB_pdo extends ADOConnection { return $obj; } - function CreateSequence($seqname='adodbseq',$startID=1) + public function createSequence($seqname='adodbseq',$startID=1) { - if(method_exists($this->_driver, 'CreateSequence')) { - return $this->_driver->CreateSequence($seqname, $startID); + if(method_exists($this->_driver, 'createSequence')) { + return $this->_driver->createSequence($seqname, $startID); } return parent::CreateSequence($seqname, $startID); @@ -478,10 +543,11 @@ class ADODB_pdo extends ADOConnection { } else { $stmt = $this->_connectionID->prepare($sql); } - #adodb_backtrace(); - #var_dump($this->_bindInputArray); + if ($stmt) { - $this->_driver->debug = $this->debug; + if (isset($this->_driver)) { + $this->_driver->debug = $this->debug; + } if ($inputarr) { $ok = $stmt->execute($inputarr); } @@ -523,6 +589,9 @@ class ADODB_pdo extends ADOConnection { function _affectedrows() { + if(method_exists($this->_driver, '_affectedrows')) + return $this->_driver->_affectedrows(); + return ($this->_stmt) ? $this->_stmt->rowCount() : 0; } @@ -813,3 +882,5 @@ class ADORecordSet_pdo extends ADORecordSet { } } + +class ADORecordSet_array_pdo extends ADORecordSet_array {} diff --git a/drivers/adodb-pdo_dblib.inc.php b/drivers/adodb-pdo_dblib.inc.php new file mode 100644 index 00000000..a9001949 --- /dev/null +++ b/drivers/adodb-pdo_dblib.inc.php @@ -0,0 +1,182 @@ +<?php +/** + * ADOdb PDO dblib driver. + * + * Released under both BSD license and Lesser GPL library license. + * Whenever there is any discrepancy between the two licenses, the BSD license + * will take precedence. + * + * @version v5.21.0-dev ??-???-2016 + * @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved. + * @copyright (c) 2019 Damien Regad, Mark Newnham and the ADOdb community + */ + +class ADODB_pdo_dblib extends ADODB_pdo +{ + var $hasTop = 'top'; + var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)'; + var $sysTimeStamp = 'GetDate()'; + var $metaDatabasesSQL = "select name from sysdatabases where name <> 'master'"; + var $metaTablesSQL="select name,case when type='U' then 'T' else 'V' end from sysobjects where (type='U' or type='V') and (name not in ('sysallocations','syscolumns','syscomments','sysdepends','sysfilegroups','sysfiles','sysfiles1','sysforeignkeys','sysfulltextcatalogs','sysindexes','sysindexkeys','sysmembers','sysobjects','syspermissions','sysprotects','sysreferences','systypes','sysusers','sysalternates','sysconstraints','syssegments','REFERENTIAL_CONSTRAINTS','CHECK_CONSTRAINTS','CONSTRAINT_TABLE_USAGE','CONSTRAINT_COLUMN_USAGE','VIEWS','VIEW_TABLE_USAGE','VIEW_COLUMN_USAGE','SCHEMATA','TABLES','TABLE_CONSTRAINTS','TABLE_PRIVILEGES','COLUMNS','COLUMN_DOMAIN_USAGE','COLUMN_PRIVILEGES','DOMAINS','DOMAIN_CONSTRAINTS','KEY_COLUMN_USAGE','dtproperties'))"; + + var $metaColumnsSQL = "SELECT c.NAME, OBJECT_NAME(c.id) as tbl_name, c.length, c.isnullable, c.status, ( CASE WHEN c.xusertype=61 THEN 0 ELSE c.xprec END), ( CASE WHEN c.xusertype=61 THEN 0 ELSE c.xscale END), ISNULL(i.is_primary_key, 0) as primary_key FROM syscolumns c INNER JOIN systypes t ON t.xusertype=c.xusertype INNER JOIN sysobjects o ON o.id=c.id LEFT JOIN sys.index_columns ic ON ic.object_id = c.id AND c.colid = ic.column_id LEFT JOIN sys.indexes i ON i.object_id = ic.object_id AND i.index_id = ic.index_id WHERE c.id = OBJECT_ID('%s') ORDER by c.colid"; + + function _init(ADODB_pdo $parentDriver) + { + $parentDriver->hasTransactions = true; + $parentDriver->_bindInputArray = true; + $parentDriver->hasInsertID = true; + $parentDriver->fmtTimeStamp = "'Y-m-d H:i:s'"; + $parentDriver->fmtDate = "'Y-m-d'"; + } + + function BeginTrans() + { + $returnval = parent::BeginTrans(); + return $returnval; + } + + function MetaColumns($table, $normalize=true) + { + $this->_findschema($table,$schema); + if ($schema) { + $dbName = $this->database; + $this->SelectDB($schema); + } + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); + + if ($schema) { + $this->SelectDB($dbName); + } + + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + if (!is_object($rs)) { + $false = false; + return $false; + } + + $retarr = array(); + while (!$rs->EOF) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->primary_key = $rs->fields[7]; + + $fld->not_null = (!$rs->fields[3]); + $fld->auto_increment = ($rs->fields[4] == 128); // sys.syscolumns status field. 0x80 = 128 ref: http://msdn.microsoft.com/en-us/library/ms186816.aspx + + if (isset($rs->fields[5]) && $rs->fields[5]) { + if ($rs->fields[5]>0) $fld->max_length = $rs->fields[5]; + $fld->scale = $rs->fields[6]; + if ($fld->scale>0) $fld->max_length += 1; + } else + $fld->max_length = $rs->fields[2]; + + if ($save == ADODB_FETCH_NUM) { + $retarr[] = $fld; + } else { + $retarr[strtoupper($fld->name)] = $fld; + } + $rs->MoveNext(); + } + + $rs->Close(); + return $retarr; + } + + function MetaTables($ttype=false,$showSchema=false,$mask=false) + { + if ($mask) { + $save = $this->metaTablesSQL; + $mask = $this->qstr(($mask)); + $this->metaTablesSQL .= " AND name like $mask"; + } + $ret = ADOConnection::MetaTables($ttype,$showSchema); + + if ($mask) { + $this->metaTablesSQL = $save; + } + return $ret; + } + + function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) + { + if ($nrows > 0 && $offset <= 0) { + $sql = preg_replace( + '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql); + + if ($secs2cache) + $rs = $this->CacheExecute($secs2cache, $sql, $inputarr); + else + $rs = $this->Execute($sql,$inputarr); + } else + $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); + + return $rs; + } + + function _query($sql,$inputarr=false) + { + $this->_connectionID->setAttribute(\PDO::ATTR_EMULATE_PREPARES , true); + if (is_array($sql)) { + $stmt = $sql[1]; + } else { + $stmt = $this->_connectionID->prepare($sql); + } + + if ($stmt) { + $this->_driver->debug = $this->debug; + if ($inputarr) { + foreach ($inputarr as $key => $value) { + if(gettype($key) == 'integer') { + $key += 1; + } + $stmt->bindValue($key, $value, $this->GetPDODataType($value)); + } + } + } + + $ok = $stmt->execute(); + + $this->_errormsg = false; + $this->_errorno = false; + + if ($ok) { + $this->_stmt = $stmt; + return $stmt; + } + + if ($stmt) { + + $arr = $stmt->errorinfo(); + if ((integer)$arr[1]) { + $this->_errormsg = $arr[2]; + $this->_errorno = $arr[1]; + } + + } else { + $this->_errormsg = false; + $this->_errorno = false; + } + return false; + } + + private function GetPDODataType($var) + { + if(gettype($var) == 'integer') { + return PDO::PARAM_INT ; + } + return PDO::PARAM_STR; + } + + function ServerInfo() + { + return ADOConnection::ServerInfo(); + } +} diff --git a/drivers/adodb-pdo_firebird.inc.php b/drivers/adodb-pdo_firebird.inc.php new file mode 100644 index 00000000..b1286029 --- /dev/null +++ b/drivers/adodb-pdo_firebird.inc.php @@ -0,0 +1,447 @@ +<?php +/** + * ADOdb PDO Firebird driver + * + * @version v5.21.0-dev ??-???-2016 + * @copyright (c) 2019 Damien Regad, Mark Newnham and the ADOdb community + * + * Released under both BSD license and Lesser GPL library license. + * Whenever there is any discrepancy between the two licenses, + * the BSD license will take precedence. See License.txt. + * + * Set tabs to 4 for best viewing. + * + * Latest version is available at http://adodb.org/ + * + * This version has only been tested on Firebird 3.0 and PHP 7 + */ + +/** + * Class ADODB_pdo_firebird + */ +class ADODB_pdo_firebird extends ADODB_pdo +{ + public $dialect = 3; + public $metaTablesSQL = "select lower(rdb\$relation_name) from rdb\$relations where rdb\$relation_name not like 'RDB\$%'"; + public $metaColumnsSQL = "select lower(a.rdb\$field_name), a.rdb\$null_flag, a.rdb\$default_source, b.rdb\$field_length, b.rdb\$field_scale, b.rdb\$field_sub_type, b.rdb\$field_precision, b.rdb\$field_type from rdb\$relation_fields a, rdb\$fields b where a.rdb\$field_source = b.rdb\$field_name and a.rdb\$relation_name = '%s' order by a.rdb\$field_position asc"; + + var $arrayClass = 'ADORecordSet_array_pdo_firebird'; + + function _init($parentDriver) + { + $this->pdoDriver = $parentDriver; + //$parentDriver->_bindInputArray = true; + //$parentDriver->hasTransactions = false; // // should be set to false because of PDO SQLite driver not supporting changing autocommit mode + //$parentDriver->hasInsertID = true; + } + + /** + * Gets the version iformation from the server + * + * @return string[] + */ + public function serverInfo() + { + $arr['dialect'] = $this->dialect; + switch ($arr['dialect']) { + case '': + case '1': + $s = 'Firebird Dialect 1'; + break; + case '2': + $s = 'Firebird Dialect 2'; + break; + default: + case '3': + $s = 'Firebird Dialect 3'; + break; + } + $arr['version'] = ADOConnection::_findvers($s); + $arr['description'] = $s; + return $arr; + } + + /** + * Returns the tables in the database. + * + * @param mixed $ttype + * @param bool $showSchema + * @param mixed $mask + * + * @return string[] + */ + public function metaTables($ttype = false, $showSchema = false, $mask = false) + { + $ret = ADOConnection::MetaTables($ttype, $showSchema); + + return $ret; + } + + public function metaColumns($table, $normalize = true) + { + global $ADODB_FETCH_MODE; + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute(sprintf($this->metaColumnsSQL, strtoupper($table))); + + $ADODB_FETCH_MODE = $save; + + if ($rs === false) { + return false; + } + + $retarr = array(); + $dialect3 = $this->dialect == 3; + while (!$rs->EOF) { //print_r($rs->fields); + $fld = new ADOFieldObject(); + $fld->name = trim($rs->fields[0]); + $this->_ConvertFieldType($fld, $rs->fields[7], $rs->fields[3], $rs->fields[4], $rs->fields[5], + $rs->fields[6], $dialect3); + if (isset($rs->fields[1]) && $rs->fields[1]) { + $fld->not_null = true; + } + if (isset($rs->fields[2])) { + + $fld->has_default = true; + $d = substr($rs->fields[2], strlen('default ')); + switch ($fld->type) { + case 'smallint': + case 'integer': + $fld->default_value = (int)$d; + break; + case 'char': + case 'blob': + case 'text': + case 'varchar': + $fld->default_value = (string)substr($d, 1, strlen($d) - 2); + break; + case 'double': + case 'float': + $fld->default_value = (float)$d; + break; + default: + $fld->default_value = $d; + break; + } + } + if ((isset($rs->fields[5])) && ($fld->type == 'blob')) { + $fld->sub_type = $rs->fields[5]; + } else { + $fld->sub_type = null; + } + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) { + $retarr[] = $fld; + } else { + $retarr[strtoupper($fld->name)] = $fld; + } + + $rs->MoveNext(); + } + $rs->Close(); + if (empty($retarr)) { + return false; + } else { + return $retarr; + } + } + + public function metaIndexes($table, $primary = false, $owner = false) + { + // save old fetch mode + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== false) { + $savem = $this->SetFetchMode(false); + } + $table = strtoupper($table); + $sql = "SELECT * FROM RDB\$INDICES WHERE RDB\$RELATION_NAME = '" . $table . "'"; + if (!$primary) { + $sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$%'"; + } else { + $sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$FOREIGN%'"; + } + + // get index details + $rs = $this->Execute($sql); + if (!is_object($rs)) { + // restore fetchmode + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + return false; + } + + $indexes = array(); + while ($row = $rs->FetchRow()) { + $index = $row[0]; + if (!isset($indexes[$index])) { + if (is_null($row[3])) { + $row[3] = 0; + } + $indexes[$index] = array( + 'unique' => ($row[3] == 1), + 'columns' => array() + ); + } + $sql = "SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '" . $index . "' ORDER BY RDB\$FIELD_POSITION ASC"; + $rs1 = $this->Execute($sql); + while ($row1 = $rs1->FetchRow()) { + $indexes[$index]['columns'][$row1[2]] = $row1[1]; + } + } + // restore fetchmode + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + + return $indexes; + } + + public function metaPrimaryKeys($table, $owner_notused = false, $internalKey = false) + { + if ($internalKey) { + return array('RDB$DB_KEY'); + } + + $table = strtoupper($table); + + $sql = 'SELECT S.RDB$FIELD_NAME AFIELDNAME + FROM RDB$INDICES I JOIN RDB$INDEX_SEGMENTS S ON I.RDB$INDEX_NAME=S.RDB$INDEX_NAME + WHERE I.RDB$RELATION_NAME=\'' . $table . '\' and I.RDB$INDEX_NAME like \'RDB$PRIMARY%\' + ORDER BY I.RDB$INDEX_NAME,S.RDB$FIELD_POSITION'; + + $a = $this->GetCol($sql, false, true); + if ($a && sizeof($a) > 0) { + return $a; + } + return false; + } + + public function createSequence($seqname = 'adodbseq', $startID = 1) + { + $ok = $this->execute("CREATE SEQUENCE $seqname"); + if (!$ok) { + return false; + } + + return $this->execute("ALTER SEQUENCE $seqname RESTART WITH " . ($startID - 1)); + } + + public function dropSequence($seqname = 'adodbseq') + { + $seqname = strtoupper($seqname); + return $this->Execute("DROP SEQUENCE $seqname"); + } + + + public function _affectedrows() + { + return fbird_affected_rows($this->_transactionID ? $this->_transactionID : $this->_connectionID); + } + + public function genId($seqname = 'adodbseq', $startID = 1) + { + $getnext = ("SELECT Gen_ID($seqname,1) FROM RDB\$DATABASE"); + $rs = @$this->execute($getnext); + if (!$rs) { + $this->execute(("CREATE SEQUENCE $seqname")); + $this->execute("ALTER SEQUENCE $seqname RESTART WITH " . ($startID - 1) . ';'); + $rs = $this->execute($getnext); + } + if ($rs && !$rs->EOF) { + $this->genID = (integer)reset($rs->fields); + } else { + $this->genID = 0; // false + } + + if ($rs) { + $rs->Close(); + } + + return $this->genID; + } + + public function selectLimit($sql, $nrows = -1, $offset = -1, $inputarr = false, $secs = 0) + { + $nrows = (integer)$nrows; + $offset = (integer)$offset; + $str = 'SELECT '; + if ($nrows >= 0) { + $str .= "FIRST $nrows "; + } + $str .= ($offset >= 0) ? "SKIP $offset " : ''; + + $sql = preg_replace('/^[ \t]*select/i', $str, $sql); + if ($secs) { + $rs = $this->cacheExecute($secs, $sql, $inputarr); + } else { + $rs = $this->execute($sql, $inputarr); + } + + return $rs; + } + + /** + * Sets the appropriate type into the $fld variable + * + * @param ADOFieldObject $fld By reference + * @param int $ftype + * @param int $flen + * @param int $fscale + * @param int $fsubtype + * @param int $fprecision + * @param bool $dialect3 + */ + final private function _convertFieldType(&$fld, $ftype, $flen, $fscale, $fsubtype, $fprecision, $dialect3) + { + $fscale = abs($fscale); + $fld->max_length = $flen; + $fld->scale = null; + switch ($ftype) { + case 7: + case 8: + if ($dialect3) { + switch ($fsubtype) { + case 0: + $fld->type = ($ftype == 7 ? 'smallint' : 'integer'); + break; + case 1: + $fld->type = 'numeric'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + case 2: + $fld->type = 'decimal'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + } // switch + } else { + if ($fscale != 0) { + $fld->type = 'decimal'; + $fld->scale = $fscale; + $fld->max_length = ($ftype == 7 ? 4 : 9); + } else { + $fld->type = ($ftype == 7 ? 'smallint' : 'integer'); + } + } + break; + case 16: + if ($dialect3) { + switch ($fsubtype) { + case 0: + $fld->type = 'decimal'; + $fld->max_length = 18; + $fld->scale = 0; + break; + case 1: + $fld->type = 'numeric'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + case 2: + $fld->type = 'decimal'; + $fld->max_length = $fprecision; + $fld->scale = $fscale; + break; + } // switch + } + break; + case 10: + $fld->type = 'float'; + break; + case 14: + $fld->type = 'char'; + break; + case 27: + if ($fscale != 0) { + $fld->type = 'decimal'; + $fld->max_length = 15; + $fld->scale = 5; + } else { + $fld->type = 'double'; + } + break; + case 35: + if ($dialect3) { + $fld->type = 'timestamp'; + } else { + $fld->type = 'date'; + } + break; + case 12: + $fld->type = 'date'; + break; + case 13: + $fld->type = 'time'; + break; + case 37: + $fld->type = 'varchar'; + break; + case 40: + $fld->type = 'cstring'; + break; + case 261: + $fld->type = 'blob'; + $fld->max_length = -1; + break; + } // switch + } +} + +/** + * Class ADORecordSet_pdo_firebird + */ +class ADORecordSet_pdo_firebird extends ADORecordSet_pdo +{ + + public $databaseType = "pdo_firebird"; + + /** + * returns the field object + * + * @param int $fieldOffset Optional field offset + * + * @return object The ADOFieldObject describing the field + */ + public function fetchField($fieldOffset = 0) + { + } +} + +/** + * Class ADORecordSet_array_pdo_firebird + */ +class ADORecordSet_array_pdo_firebird extends ADORecordSet_array_pdo +{ + public $databaseType = "pdo_firebird"; + public $canSeek = true; + + /** + * returns the field object + * + * @param int $fieldOffset Optional field offset + * + * @return object The ADOFieldObject describing the field + */ + public function fetchField($fieldOffset = 0) + { + + $fld = new ADOFieldObject; + $fld->name = $fieldOffset; + $fld->type = 'C'; + $fld->max_length = 0; + + // This needs to be populated from the metadata + $fld->not_null = false; + $fld->has_default = false; + $fld->default_value = 'null'; + + return $fld; + } +} diff --git a/drivers/adodb-pdo_mysql.inc.php b/drivers/adodb-pdo_mysql.inc.php index 17c6f70f..b18aee9f 100644 --- a/drivers/adodb-pdo_mysql.inc.php +++ b/drivers/adodb-pdo_mysql.inc.php @@ -21,7 +21,10 @@ class ADODB_pdo_mysql extends ADODB_pdo { var $sysDate = 'CURDATE()'; var $sysTimeStamp = 'NOW()'; var $hasGenID = true; - var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; + var $_genIDSQL = "UPDATE %s SET id=LAST_INSERT_ID(id+1);"; + var $_genSeqSQL = "CREATE TABLE if NOT EXISTS %s (id int not null)"; + var $_genSeqCountSQL = "SELECT count(*) FROM %s"; + var $_genSeq2SQL = "INSERT INTO %s VALUES (%s)"; var $_dropSeqSQL = "drop table %s"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; var $nameQuote = '`'; @@ -310,4 +313,41 @@ class ADODB_pdo_mysql extends ADODB_pdo { } return $s; } + + function GenID($seqname='adodbseq',$startID=1) + { + $getnext = sprintf($this->_genIDSQL,$seqname); + $holdtransOK = $this->_transOK; // save the current status + $rs = @$this->Execute($getnext); + if (!$rs) { + if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset + $this->Execute(sprintf($this->_genSeqSQL,$seqname)); + $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL,$seqname)); + if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); + $rs = $this->Execute($getnext); + } + + if ($rs) { + $this->genID = $this->_connectionID->lastInsertId($seqname); + $rs->Close(); + } else { + $this->genID = 0; + } + + return $this->genID; + } + + + function createSequence($seqname='adodbseq',$startID=1) + { + if (empty($this->_genSeqSQL)) { + return false; + } + $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); + if (!$ok) { + return false; + } + + return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); + } } diff --git a/drivers/adodb-pdo_pgsql.inc.php b/drivers/adodb-pdo_pgsql.inc.php index f4c4724a..5ea81c2b 100644 --- a/drivers/adodb-pdo_pgsql.inc.php +++ b/drivers/adodb-pdo_pgsql.inc.php @@ -229,4 +229,64 @@ select viewname,'V' from pg_views where viewname like $mask"; } + function BeginTrans() + { + if (!$this->hasTransactions) { + return false; + } + if ($this->transOff) { + return true; + } + $this->transCnt += 1; + + return $this->_connectionID->beginTransaction(); + } + + function CommitTrans($ok = true) + { + if (!$this->hasTransactions) { + return false; + } + if ($this->transOff) { + return true; + } + if (!$ok) { + return $this->RollbackTrans(); + } + if ($this->transCnt) { + $this->transCnt -= 1; + } + $this->_autocommit = true; + + $ret = $this->_connectionID->commit(); + return $ret; + } + + function RollbackTrans() + { + if (!$this->hasTransactions) { + return false; + } + if ($this->transOff) { + return true; + } + if ($this->transCnt) { + $this->transCnt -= 1; + } + $this->_autocommit = true; + + $ret = $this->_connectionID->rollback(); + return $ret; + } + + function SetTransactionMode( $transaction_mode ) + { + $this->_transmode = $transaction_mode; + if (empty($transaction_mode)) { + $this->_connectionID->query('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); + return; + } + if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; + $this->_connectionID->query("SET TRANSACTION ".$transaction_mode); + } } diff --git a/drivers/adodb-pdo_sqlsrv.inc.php b/drivers/adodb-pdo_sqlsrv.inc.php index 869e8e18..ba5180ec 100644 --- a/drivers/adodb-pdo_sqlsrv.inc.php +++ b/drivers/adodb-pdo_sqlsrv.inc.php @@ -5,10 +5,10 @@ */ class ADODB_pdo_sqlsrv extends ADODB_pdo { - var $hasTop = 'top'; var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)'; var $sysTimeStamp = 'GetDate()'; + var $arrayClass = 'ADORecordSet_array_pdo_sqlsrv'; function _init(ADODB_pdo $parentDriver) { @@ -45,5 +45,120 @@ class ADODB_pdo_sqlsrv extends ADODB_pdo { return ADOConnection::ServerInfo(); } +} + +class ADORecordSet_pdo_sqlsrv extends ADORecordSet_pdo +{ + + public $databaseType = "pdo_sqlsrv"; + + /** + * returns the field object + * + * @param int $fieldOffset Optional field offset + * + * @return object The ADOFieldObject describing the field + */ + public function fetchField($fieldOffset = 0) + { + + // Default behavior allows passing in of -1 offset, which crashes the method + if ($fieldOffset == -1) { + $fieldOffset++; + } + + $o = new ADOFieldObject(); + $arr = @$this->_queryID->getColumnMeta($fieldOffset); + + if (!$arr) { + $o->name = 'bad getColumnMeta()'; + $o->max_length = -1; + $o->type = 'VARCHAR'; + $o->precision = 0; + return $o; + } + $o->name = $arr['name']; + if (isset($arr['sqlsrv:decl_type']) && $arr['sqlsrv:decl_type'] <> "null") { + // Use the SQL Server driver specific value + $o->type = $arr['sqlsrv:decl_type']; + } else { + $o->type = adodb_pdo_type($arr['pdo_type']); + } + $o->max_length = $arr['len']; + $o->precision = $arr['precision']; + + switch (ADODB_ASSOC_CASE) { + case ADODB_ASSOC_CASE_LOWER: + $o->name = strtolower($o->name); + break; + case ADODB_ASSOC_CASE_UPPER: + $o->name = strtoupper($o->name); + break; + } + + return $o; + } +} + +class ADORecordSet_array_pdo_sqlsrv extends ADORecordSet_array_pdo +{ + + /** + * returns the field object + * + * Note that this is a direct copy of the ADORecordSet_pdo_sqlsrv method + * + * @param int $fieldOffset Optional field offset + * + * @return object The ADOfieldobject describing the field + */ + public function fetchField($fieldOffset = 0) + { + // Default behavior allows passing in of -1 offset, which crashes the method + if ($fieldOffset == -1) { + $fieldOffset++; + } + $o = new ADOFieldObject(); + $arr = @$this->_queryID->getColumnMeta($fieldOffset); + + if (!$arr) { + $o->name = 'bad getColumnMeta()'; + $o->max_length = -1; + $o->type = 'VARCHAR'; + $o->precision = 0; + return $o; + } + $o->name = $arr['name']; + if (isset($arr['sqlsrv:decl_type']) && $arr['sqlsrv:decl_type'] <> "null") { + // Use the SQL Server driver specific value + $o->type = $arr['sqlsrv:decl_type']; + } else { + $o->type = adodb_pdo_type($arr['pdo_type']); + } + $o->max_length = $arr['len']; + $o->precision = $arr['precision']; + + switch (ADODB_ASSOC_CASE) { + case ADODB_ASSOC_CASE_LOWER: + $o->name = strtolower($o->name); + break; + case ADODB_ASSOC_CASE_UPPER: + $o->name = strtoupper($o->name); + break; + } + + return $o; + } + + function SetTransactionMode( $transaction_mode ) + { + $this->_transmode = $transaction_mode; + if (empty($transaction_mode)) { + $this->_connectionID->query('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); + return; + } + if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; + $this->_connectionID->query("SET TRANSACTION ".$transaction_mode); + } } diff --git a/drivers/adodb-postgres.inc.php b/drivers/adodb-postgres.inc.php index 784227d3..e20b70cb 100644 --- a/drivers/adodb-postgres.inc.php +++ b/drivers/adodb-postgres.inc.php @@ -9,6 +9,7 @@ Set tabs to 4. NOTE: Since 3.31, this file is no longer used, and the "postgres" driver is - remapped to "postgres7". Maintaining multiple postgres drivers is no easy - job, so hopefully this will ensure greater consistency and fewer bugs. + remapped to lastest available postgres version. Maintaining multiple + postgres drivers is no easy job, so hopefully this will ensure greater + consistency and fewer bugs. */ diff --git a/drivers/adodb-postgres64.inc.php b/drivers/adodb-postgres64.inc.php index c5302e29..4f57af13 100644 --- a/drivers/adodb-postgres64.inc.php +++ b/drivers/adodb-postgres64.inc.php @@ -124,19 +124,40 @@ class ADODB_postgres64 extends ADOConnection{ // to know what the concequences are. The other values are correct (wheren't in 0.94) // -- Freek Dijkstra - function __construct() + /** + * Retrieve Server information. + * In addition to server version and description, the function also returns + * the client version. + * @param bool $detailed If true, retrieve detailed version string (executes + * a SQL query) in addition to the version number + * @return array|bool Server info or false if version could not be retrieved + * e.g. if there is no active connection + */ + function ServerInfo($detailed = true) { - // changes the metaColumnsSQL, adds columns: attnum[6] - } + if (empty($this->version['version'])) { + // We don't have a connection, so we can't retrieve server info + if (!$this->_connectionID) { + return false; + } - function ServerInfo() - { - if (isset($this->version)) return $this->version; + $version = pg_version($this->_connectionID); + $this->version = array( + // If PHP has been compiled with PostgreSQL 7.3 or lower, then + // server version is not set so we use pg_parameter_status() + // which includes logic to obtain values server_version + 'version' => isset($version['server']) + ? $version['server'] + : pg_parameter_status($this->_connectionID, 'server_version'), + 'client' => $version['client'], + 'description' => null, + ); + } + if ($detailed && $this->version['description'] === null) { + $this->version['description'] = $this->GetOne('select version()'); + } - $arr['description'] = $this->GetOne("select version()"); - $arr['version'] = ADOConnection::_findvers($arr['description']); - $this->version = $arr; - return $arr; + return $this->version; } function IfNull( $field, $ifNull ) @@ -249,16 +270,12 @@ class ADODB_postgres64 extends ADOConnection{ if (is_bool($s)) return $s ? 'true' : 'false'; if (!$magic_quotes) { - if (ADODB_PHPVER >= 0x5200 && $this->_connectionID) { - return "'".pg_escape_string($this->_connectionID,$s)."'"; - } - if (ADODB_PHPVER >= 0x4200) { - return "'".pg_escape_string($s)."'"; + if ($this->_connectionID) { + return "'" . pg_escape_string($this->_connectionID, $s) . "'"; } - if ($this->replaceQuote[0] == '\\'){ - $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\\000"),$s); + else { + return "'" . pg_escape_string($s) . "'"; } - return "'".str_replace("'",$this->replaceQuote,$s)."'"; } // undo magic quotes for " @@ -393,7 +410,7 @@ class ADODB_postgres64 extends ADOConnection{ } /* - Hueristic - not guaranteed to work. + Heuristic - not guaranteed to work. */ function GuessOID($oid) { @@ -430,24 +447,20 @@ class ADODB_postgres64 extends ADOConnection{ return $realblob; } - /* - See http://www.postgresql.org/idocs/index.php?datatype-binary.html - - NOTE: SQL string literals (input strings) must be preceded with two backslashes - due to the fact that they must pass through two parsers in the PostgreSQL - backend. - */ + /** + * Encode binary value prior to DB storage. + * + * See https://www.postgresql.org/docs/current/static/datatype-binary.html + * + * NOTE: SQL string literals (input strings) must be preceded with two + * backslashes due to the fact that they must pass through two parsers in + * the PostgreSQL backend. + * + * @param string $blob + */ function BlobEncode($blob) { - if (ADODB_PHPVER >= 0x5200) return pg_escape_bytea($this->_connectionID, $blob); - if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob); - - /*92=backslash, 0=null, 39=single-quote*/ - $badch = array(chr(92),chr(0),chr(39)); # \ null ' - $fixch = array('\\\\134','\\\\000','\\\\047'); - return adodb_str_replace($badch,$fixch,$blob); - - // note that there is a pg_escape_bytea function only for php 4.2.0 or later + return pg_escape_bytea($this->_connectionID, $blob); } // assumes bytea for blob, and varchar for clob @@ -609,12 +622,12 @@ class ADODB_postgres64 extends ADOConnection{ function Param($name,$type='C') { if ($name) { - $this->_pnum += 1; + $this->_pnum++; } else { // Reset param num if $name is false - $this->_pnum = 1; + $this->_pnum = 0; } - return '$'.$this->_pnum; + return '$' . $this->_pnum; } function MetaIndexes ($table, $primary = FALSE, $owner = false) @@ -729,9 +742,9 @@ class ADODB_postgres64 extends ADOConnection{ if ($this->_connectionID === false) return false; $this->Execute("set datestyle='ISO'"); - $info = $this->ServerInfo(); - $this->pgVersion = (float) substr($info['version'],0,3); - if ($this->pgVersion >= 7.1) { // good till version 999 + $info = $this->ServerInfo(false); + + if (version_compare($info['version'], '7.1', '>=')) { $this->_nestedSQL = true; } @@ -739,8 +752,11 @@ class ADODB_postgres64 extends ADOConnection{ # PHP does not handle 'hex' properly ('x74657374' is returned as 't657374') # https://bugs.php.net/bug.php?id=59831 states this is in fact not a bug, # so we manually set bytea_output - if ( !empty($this->connection->noBlobs) && version_compare($info['version'], '9.0', '>=')) { - $this->Execute('set bytea_output=escape'); + if (!empty($this->connection->noBlobs) && version_compare($info['version'], '9.0', '>=')) { + $version = pg_version($this->connectionID); + if (version_compare($info['client'], '9.2', '<')) { + $this->Execute('set bytea_output=escape'); + } } return true; @@ -848,20 +864,23 @@ class ADODB_postgres64 extends ADOConnection{ /* Returns: the last error message from previous database operation */ function ErrorMsg() { - if ($this->_errorMsg !== false) return $this->_errorMsg; - if (ADODB_PHPVER >= 0x4300) { - if (!empty($this->_resultid)) { - $this->_errorMsg = @pg_result_error($this->_resultid); - if ($this->_errorMsg) return $this->_errorMsg; + if ($this->_errorMsg !== false) { + return $this->_errorMsg; + } + + if (!empty($this->_resultid)) { + $this->_errorMsg = @pg_result_error($this->_resultid); + if ($this->_errorMsg) { + return $this->_errorMsg; } + } - if (!empty($this->_connectionID)) { - $this->_errorMsg = @pg_last_error($this->_connectionID); - } else $this->_errorMsg = $this->_errconnect(); + if (!empty($this->_connectionID)) { + $this->_errorMsg = @pg_last_error($this->_connectionID); } else { - if (empty($this->_connectionID)) $this->_errconnect(); - else $this->_errorMsg = @pg_errormessage($this->_connectionID); + $this->_errorMsg = $this->_errconnect(); } + return $this->_errorMsg; } @@ -1068,6 +1087,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ case 'NAME': case 'BPCHAR': case '_VARCHAR': + case 'CIDR': case 'INET': case 'MACADDR': if ($len <= $this->blobSize) return 'C'; @@ -1111,7 +1131,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ return 'R'; default: - return 'N'; + return ADODB_DEFAULT_METATYPE; } } diff --git a/drivers/adodb-postgres7.inc.php b/drivers/adodb-postgres7.inc.php index 9d643a71..6acf6cfb 100644 --- a/drivers/adodb-postgres7.inc.php +++ b/drivers/adodb-postgres7.inc.php @@ -101,7 +101,7 @@ class ADODB_postgres7 extends ADODB_postgres64 { if (ADODB_ASSOC_CASE !== ADODB_ASSOC_CASE_NATIVE) { $this->rsPrefix .= 'assoc_'; } - $this->_bindInputArray = PHP_VERSION >= 5.1; + $this->_bindInputArray = true; } @@ -309,12 +309,6 @@ class ADORecordSet_postgres7 extends ADORecordSet_postgres64{ var $databaseType = "postgres7"; - - function __construct($queryID, $mode=false) - { - parent::__construct($queryID, $mode); - } - // 10% speedup to move MoveNext to child class function MoveNext() { @@ -341,11 +335,6 @@ class ADORecordSet_assoc_postgres7 extends ADORecordSet_postgres64{ var $databaseType = "postgres7"; - function __construct($queryID, $mode=false) - { - parent::__construct($queryID, $mode); - } - function _fetch() { if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) { diff --git a/drivers/adodb-proxy.inc.php b/drivers/adodb-proxy.inc.php index 840b61e3..ed53195d 100644 --- a/drivers/adodb-proxy.inc.php +++ b/drivers/adodb-proxy.inc.php @@ -15,19 +15,16 @@ if (!defined('ADODB_DIR')) die(); if (! defined("_ADODB_PROXY_LAYER")) { - define("_ADODB_PROXY_LAYER", 1 ); - include(ADODB_DIR."/drivers/adodb-csv.inc.php"); + define("_ADODB_PROXY_LAYER", 1 ); + include_once(ADODB_DIR."/drivers/adodb-csv.inc.php"); - class ADODB_proxy extends ADODB_csv { - var $databaseType = 'proxy'; - var $databaseProvider = 'csv'; - } - class ADORecordset_proxy extends ADORecordset_csv { +class ADODB_proxy extends ADODB_csv { + var $databaseType = 'proxy'; + var $databaseProvider = 'csv'; +} + +class ADORecordset_proxy extends ADORecordset_csv { var $databaseType = "proxy"; +} - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } - }; } // define diff --git a/drivers/adodb-sapdb.inc.php b/drivers/adodb-sapdb.inc.php index 21913bc2..085ec3e6 100644 --- a/drivers/adodb-sapdb.inc.php +++ b/drivers/adodb-sapdb.inc.php @@ -18,7 +18,7 @@ Set tabs to 4 for best viewing. if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } if (!defined('ADODB_SAPDB')){ define('ADODB_SAPDB',1); @@ -33,12 +33,6 @@ class ADODB_SAPDB extends ADODB_odbc { var $hasInsertId = true; var $_bindInputArray = true; - function __construct() - { - //if (strncmp(PHP_OS,'WIN',3) === 0) $this->curmode = SQL_CUR_USE_ODBC; - parent::__construct(); - } - function ServerInfo() { $info = ADODB_odbc::ServerInfo(); @@ -55,7 +49,7 @@ class ADODB_SAPDB extends ADODB_odbc { return $this->GetCol("SELECT columnname FROM COLUMNS WHERE tablename=$table AND mode='KEY' ORDER BY pos"); } - function MetaIndexes ($table, $primary = FALSE, $owner = false) + function MetaIndexes ($table, $primary = FALSE, $owner = false) { $table = $this->Quote(strtoupper($table)); @@ -65,43 +59,43 @@ class ADODB_SAPDB extends ADODB_odbc { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - if ($this->fetchMode !== FALSE) { - $savem = $this->SetFetchMode(FALSE); - } + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } - $rs = $this->Execute($sql); - if (isset($savem)) { - $this->SetFetchMode($savem); - } - $ADODB_FETCH_MODE = $save; + $rs = $this->Execute($sql); + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; - if (!is_object($rs)) { - return FALSE; - } + if (!is_object($rs)) { + return FALSE; + } $indexes = array(); while ($row = $rs->FetchRow()) { - $indexes[$row[0]]['unique'] = $row[1] == 'UNIQUE'; - $indexes[$row[0]]['columns'][] = $row[2]; - } + $indexes[$row[0]]['unique'] = $row[1] == 'UNIQUE'; + $indexes[$row[0]]['columns'][] = $row[2]; + } if ($primary) { $indexes['SYSPRIMARYKEYINDEX'] = array( 'unique' => True, // by definition 'columns' => $this->GetCol("SELECT columnname FROM COLUMNS WHERE tablename=$table AND mode='KEY' ORDER BY pos"), ); } - return $indexes; + return $indexes; } function MetaColumns ($table, $normalize = true) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - if ($this->fetchMode !== FALSE) { - $savem = $this->SetFetchMode(FALSE); - } + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } $table = $this->Quote(strtoupper($table)); $retarr = array(); @@ -134,10 +128,10 @@ class ADODB_SAPDB extends ADODB_odbc { } $retarr[$fld->name] = $fld; } - if (isset($savem)) { - $this->SetFetchMode($savem); - } - $ADODB_FETCH_MODE = $save; + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; return $retarr; } @@ -158,13 +152,13 @@ class ADODB_SAPDB extends ADODB_odbc { /* SelectLimit implementation problems: - The following will return random 10 rows as order by performed after "WHERE rowno<10" - which is not ideal... + The following will return random 10 rows as order by performed after "WHERE rowno<10" + which is not ideal... - select * from table where rowno < 10 order by 1 + select * from table where rowno < 10 order by 1 - This means that we have to use the adoconnection base class SelectLimit when - there is an "order by". + This means that we have to use the adoconnection base class SelectLimit when + there is an "order by". See http://listserv.sap.com/pipermail/sapdb.general/2002-January/010405.html */ @@ -172,14 +166,10 @@ class ADODB_SAPDB extends ADODB_odbc { }; -class ADORecordSet_sapdb extends ADORecordSet_odbc { +class ADORecordSet_sapdb extends ADORecordSet_odbc { var $databaseType = "sapdb"; - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } } } //define diff --git a/drivers/adodb-sqlanywhere.inc.php b/drivers/adodb-sqlanywhere.inc.php index 82afbcea..3909767e 100644 --- a/drivers/adodb-sqlanywhere.inc.php +++ b/drivers/adodb-sqlanywhere.inc.php @@ -47,7 +47,7 @@ Set tabs to 4 for best viewing. if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } if (!defined('ADODB_SYBASE_SQLANYWHERE')){ @@ -153,11 +153,6 @@ if (!defined('ADODB_SYBASE_SQLANYWHERE')){ var $databaseType = "sqlanywhere"; - function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } - }; //class diff --git a/drivers/adodb-sqlite.inc.php b/drivers/adodb-sqlite.inc.php index 2aff6562..8c0426b3 100644 --- a/drivers/adodb-sqlite.inc.php +++ b/drivers/adodb-sqlite.inc.php @@ -22,6 +22,7 @@ if (!defined('ADODB_DIR')) die(); class ADODB_sqlite extends ADOConnection { var $databaseType = "sqlite"; + var $dataProvider = "sqlite"; var $replaceQuote = "''"; // string to use to replace quotes var $concat_operator='||'; var $_errorNo = 0; @@ -33,10 +34,6 @@ class ADODB_sqlite extends ADOConnection { var $sysTimeStamp = "adodb_date('Y-m-d H:i:s')"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; - function __construct() - { - } - function ServerInfo() { $arr['version'] = sqlite_libversion(); @@ -355,6 +352,63 @@ class ADODB_sqlite extends ADOConnection { return $indexes; } + /** + * Returns the maximum size of a MetaType C field. Because of the + * database design, sqlite places no limits on the size of data inserted + * + * @return int + */ + function charMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + + /** + * Returns the maximum size of a MetaType X field. Because of the + * database design, sqlite places no limits on the size of data inserted + * + * @return int + */ + function textMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + + /* + * Converts a date to a month only field and pads it to 2 characters + * + * @param str $fld The name of the field to process + * @return str The SQL Statement + */ + function month($fld) + { + $x = "strftime('%m',$fld)"; + + return $x; + } + + /* + * Converts a date to a day only field and pads it to 2 characters + * + * @param str $fld The name of the field to process + * @return str The SQL Statement + */ + function day($fld) { + $x = "strftime('%d',$fld)"; + return $x; + } + + /* + * Converts a date to a year only field + * + * @param str $fld The name of the field to process + * @return str The SQL Statement + */ + function year($fld) { + $x = "strftime('%Y',$fld)"; + + return $x; + } } /*-------------------------------------------------------------------------------------- diff --git a/drivers/adodb-sqlite3.inc.php b/drivers/adodb-sqlite3.inc.php index f572c1e1..f4660d95 100644 --- a/drivers/adodb-sqlite3.inc.php +++ b/drivers/adodb-sqlite3.inc.php @@ -22,6 +22,7 @@ if (!defined('ADODB_DIR')) die(); class ADODB_sqlite3 extends ADOConnection { var $databaseType = "sqlite3"; + var $dataProvider = "sqlite"; var $replaceQuote = "''"; // string to use to replace quotes var $concat_operator='||'; var $_errorNo = 0; @@ -33,10 +34,6 @@ class ADODB_sqlite3 extends ADOConnection { var $sysTimeStamp = "adodb_date('Y-m-d H:i:s')"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; - function __construct() - { - } - function ServerInfo() { $version = SQLite3::version(); @@ -82,6 +79,73 @@ class ADODB_sqlite3 extends ADOConnection { return !empty($ret); } + function metaType($t,$len=-1,$fieldobj=false) + { + + if (is_object($t)) + { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + + $t = strtoupper($t); + + /* + * We are using the Sqlite affinity method here + * @link https://www.sqlite.org/datatype3.html + */ + $affinity = array( + 'INT'=>'INTEGER', + 'INTEGER'=>'INTEGER', + 'TINYINT'=>'INTEGER', + 'SMALLINT'=>'INTEGER', + 'MEDIUMINT'=>'INTEGER', + 'BIGINT'=>'INTEGER', + 'UNSIGNED BIG INT'=>'INTEGER', + 'INT2'=>'INTEGER', + 'INT8'=>'INTEGER', + + 'CHARACTER'=>'TEXT', + 'VARCHAR'=>'TEXT', + 'VARYING CHARACTER'=>'TEXT', + 'NCHAR'=>'TEXT', + 'NATIVE CHARACTER'=>'TEXT', + 'NVARCHAR'=>'TEXT', + 'TEXT'=>'TEXT', + 'CLOB'=>'TEXT', + + 'BLOB'=>'BLOB', + + 'REAL'=>'REAL', + 'DOUBLE'=>'REAL', + 'DOUBLE PRECISION'=>'REAL', + 'FLOAT'=>'REAL', + + 'NUMERIC'=>'NUMERIC', + 'DECIMAL'=>'NUMERIC', + 'BOOLEAN'=>'NUMERIC', + 'DATE'=>'NUMERIC', + 'DATETIME'=>'NUMERIC' + ); + + if (!isset($affinity[$t])) + return ADODB_DEFAULT_METATYPE; + + $subt = $affinity[$t]; + /* + * Now that we have subclassed the provided data down + * the sqlite 'affinity', we convert to ADOdb metatype + */ + + $subclass = array('INTEGER'=>'I', + 'TEXT'=>'X', + 'BLOB'=>'B', + 'REAL'=>'N', + 'NUMERIC'=>'N'); + + return $subclass[$subt]; + } // mark newnham function MetaColumns($table, $normalize=true) { @@ -129,6 +193,60 @@ class ADODB_sqlite3 extends ADOConnection { return $arr; } + function metaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) + { + global $ADODB_FETCH_MODE; + if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC + || $this->fetchMode == ADODB_FETCH_ASSOC) + $associative = true; + + /* + * Read sqlite master to find foreign keys + */ + $sql = "SELECT sql + FROM ( + SELECT sql sql, type type, tbl_name tbl_name, name name + FROM sqlite_master + ) + WHERE type != 'meta' + AND sql NOTNULL + AND LOWER(name) ='" . strtolower($table) . "'"; + + $tableSql = $this->getOne($sql); + + $fkeyList = array(); + $ylist = preg_split("/,+/",$tableSql); + foreach ($ylist as $y) + { + if (!preg_match('/FOREIGN/',$y)) + continue; + + $matches = false; + preg_match_all('/\((.+?)\)/i',$y,$matches); + $tmatches = false; + preg_match_all('/REFERENCES (.+?)\(/i',$y,$tmatches); + + if ($associative) + { + if (!isset($fkeyList[$tmatches[1][0]])) + $fkeyList[$tmatches[1][0]] = array(); + $fkeyList[$tmatches[1][0]][$matches[1][0]] = $matches[1][1]; + } + else + $fkeyList[$tmatches[1][0]][] = $matches[1][0] . '=' . $matches[1][1]; + } + + if ($associative) + { + if ($upper) + $fkeyList = array_change_key_case($fkeyList,CASE_UPPER); + else + $fkeyList = array_change_key_case($fkeyList,CASE_LOWER); + } + return $fkeyList; + } + + function _init($parentDriver) { $parentDriver->hasTransactions = false; @@ -160,11 +278,22 @@ class ADODB_sqlite3 extends ADOConnection { function SQLDate($fmt, $col=false) { + /* + * In order to map the values correctly, we must ensure the proper + * casing for certain fields + * Y must be UC, because y is a 2 digit year + * d must be LC, because D is 3 char day + * A must be UC because a is non-portable am + * Q must be UC because q means nothing + */ + $fromChars = array('y','D','a','q'); + $toChars = array('Y','d','A','Q'); + $fmt = str_replace($fromChars,$toChars,$fmt); + $fmt = $this->qstr($fmt); return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)"; } - function _createFunctions() { $this->_connectionID->createFunction('adodb_date', 'adodb_date', 1); @@ -299,7 +428,7 @@ class ADODB_sqlite3 extends ADOConnection { if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } - $SQL=sprintf("SELECT name,sql FROM sqlite_master WHERE type='index' AND tbl_name='%s'", strtolower($table)); + $SQL=sprintf("SELECT name,sql FROM sqlite_master WHERE type='index' AND LOWER(tbl_name)='%s'", strtolower($table)); $rs = $this->Execute($SQL); if (!is_object($rs)) { if (isset($savem)) { @@ -321,15 +450,12 @@ class ADODB_sqlite3 extends ADOConnection { ); } /** - * There must be a more elegant way of doing this, - * the index elements appear in the SQL statement + * The index elements appear in the SQL statement * in cols[1] between parentheses * e.g CREATE UNIQUE INDEX ware_0 ON warehouse (org,warehouse) */ - $cols = explode("(",$row[1]); - $cols = explode(")",$cols[1]); - array_pop($cols); - $indexes[$row[0]]['columns'] = $cols; + preg_match_all('/\((.*)\)/',$row[1],$indexExpression); + $indexes[$row[0]]['columns'] = array_map('trim',explode(',',$indexExpression[1][0])); } if (isset($savem)) { $this->SetFetchMode($savem); @@ -338,6 +464,72 @@ class ADODB_sqlite3 extends ADOConnection { return $indexes; } + /** + * Returns the maximum size of a MetaType C field. Because of the + * database design, sqlite places no limits on the size of data inserted + * + * @return int + */ + function charMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + + /** + * Returns the maximum size of a MetaType X field. Because of the + * database design, sqlite places no limits on the size of data inserted + * + * @return int + */ + function textMax() + { + return ADODB_STRINGMAX_NOLIMIT; + } + + /** + * Converts a date to a month only field and pads it to 2 characters + * + * This uses the more efficient strftime native function to process + * + * @param str $fld The name of the field to process + * + * @return str The SQL Statement + */ + function month($fld) + { + $x = "strftime('%m',$fld)"; + return $x; + } + + /** + * Converts a date to a day only field and pads it to 2 characters + * + * This uses the more efficient strftime native function to process + * + * @param str $fld The name of the field to process + * + * @return str The SQL Statement + */ + function day($fld) { + $x = "strftime('%d',$fld)"; + return $x; + } + + /** + * Converts a date to a year only field + * + * This uses the more efficient strftime native function to process + * + * @param str $fld The name of the field to process + * + * @return str The SQL Statement + */ + function year($fld) + { + $x = "strftime('%Y',$fld)"; + return $x; + } + } /*-------------------------------------------------------------------------------------- diff --git a/drivers/adodb-sqlitepo.inc.php b/drivers/adodb-sqlitepo.inc.php index f8dc121b..f9c99835 100644 --- a/drivers/adodb-sqlitepo.inc.php +++ b/drivers/adodb-sqlitepo.inc.php @@ -35,11 +35,6 @@ class ADORecordset_sqlitepo extends ADORecordset_sqlite { var $databaseType = 'sqlitepo'; - function __construct($queryID,$mode=false) - { - parent::__construct($queryID,$mode); - } - // Modified to strip table names from returned fields function _fetch($ignore_fields=false) { diff --git a/drivers/adodb-sybase.inc.php b/drivers/adodb-sybase.inc.php index 8e122d67..dbdde51e 100644 --- a/drivers/adodb-sybase.inc.php +++ b/drivers/adodb-sybase.inc.php @@ -44,10 +44,6 @@ class ADODB_sybase extends ADOConnection { var $port; - function __construct() - { - } - // might require begintrans -- committrans function _insertid() { @@ -171,7 +167,7 @@ class ADODB_sybase extends ADOConnection { { global $ADODB_COUNTRECS; - if ($ADODB_COUNTRECS == false && ADODB_PHPVER >= 0x4300) + if ($ADODB_COUNTRECS == false) return sybase_unbuffered_query($sql,$this->_connectionID); else return sybase_query($sql,$this->_connectionID); @@ -320,7 +316,7 @@ class ADORecordset_sybase extends ADORecordSet { } if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC; else $this->fetchMode = $mode; - parent::__construct($id,$mode); + parent::__construct($id); } /* Returns: an object containing field information. @@ -393,12 +389,8 @@ class ADORecordset_sybase extends ADORecordSet { } class ADORecordSet_array_sybase extends ADORecordSet_array { - function __construct($id=-1) - { - parent::__construct($id); - } - // sybase/mssql uses a default date like Dec 30 2000 12:00AM + // sybase/mssql uses a default date like Dec 30 2000 12:00AM static function UnixDate($v) { global $ADODB_sybase_mths; diff --git a/drivers/adodb-sybase_ase.inc.php b/drivers/adodb-sybase_ase.inc.php index 22d15dd3..13211f5c 100644 --- a/drivers/adodb-sybase_ase.inc.php +++ b/drivers/adodb-sybase_ase.inc.php @@ -22,10 +22,6 @@ class ADODB_sybase_ase extends ADODB_sybase { var $metaColumnsSQL = "SELECT syscolumns.name AS field_name, systypes.name AS type, systypes.length AS width FROM sysobjects, syscolumns, systypes WHERE sysobjects.name='%s' AND syscolumns.id = sysobjects.id AND systypes.type=syscolumns.type"; var $metaDatabasesSQL ="SELECT a.name FROM master.dbo.sysdatabases a, master.dbo.syslogins b WHERE a.suid = b.suid and a.name like '%' and a.name != 'tempdb' and a.status3 != 256 order by 1"; - function __construct() - { - } - // split the Views, Tables and procedures. function MetaTables($ttype=false,$showSchema=false,$mask=false) { @@ -111,10 +107,5 @@ class ADODB_sybase_ase extends ADODB_sybase { } class adorecordset_sybase_ase extends ADORecordset_sybase { -var $databaseType = "sybase_ase"; -function __construct($id,$mode=false) - { - parent::__construct($id,$mode); - } - + var $databaseType = "sybase_ase"; } diff --git a/drivers/adodb-text.inc.php b/drivers/adodb-text.inc.php index 97f3ff25..e16cf405 100644 --- a/drivers/adodb-text.inc.php +++ b/drivers/adodb-text.inc.php @@ -90,10 +90,6 @@ class ADODB_text extends ADOConnection { var $_reznames; var $_reztypes; - function __construct() - { - } - function RSRecordCount() { if (!empty($this->_rezarray)) return sizeof($this->_rezarray); @@ -325,7 +321,7 @@ class ADODB_text extends ADOConnection { // check for desc sort $orderby = substr($orderby,strlen($col)+1); - $arr == array(); + $arr = array(); preg_match('/([A-Z_0-9]*)/i',$orderby,$arr); if (trim($arr[1]) == 'DESC') $sortf = 'adodb_cmpr'; diff --git a/drivers/adodb-vfp.inc.php b/drivers/adodb-vfp.inc.php index 3f10f175..7e70d01a 100644 --- a/drivers/adodb-vfp.inc.php +++ b/drivers/adodb-vfp.inc.php @@ -17,7 +17,7 @@ Set tabs to 4 for best viewing. if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); + include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } if (!defined('ADODB_VFP')){ define('ADODB_VFP',1); @@ -69,11 +69,6 @@ class ADORecordSet_vfp extends ADORecordSet_odbc { var $databaseType = "vfp"; - function __construct($id,$mode=false) - { - return parent::__construct($id,$mode); - } - function MetaType($t, $len = -1, $fieldobj = false) { if (is_object($t)) { @@ -95,7 +90,7 @@ class ADORecordSet_vfp extends ADORecordSet_odbc { case 'I': return 'I'; - default: return 'N'; + default: return ADODB_DEFAULT_METATYPE; } } } diff --git a/lang/adodb-de.inc.php b/lang/adodb-de.inc.php index dca4ffef..99dde149 100644 --- a/lang/adodb-de.inc.php +++ b/lang/adodb-de.inc.php @@ -1,32 +1,32 @@ <?php // contributed by "Heinz Hombergs" <opn@hhombergs.de> $ADODB_LANG_ARRAY = array ( - 'LANG' => 'de', - DB_ERROR => 'Unbekannter Fehler', - DB_ERROR_ALREADY_EXISTS => 'existiert bereits', - DB_ERROR_CANNOT_CREATE => 'kann nicht erstellen', - DB_ERROR_CANNOT_DELETE => 'kann nicht löschen', - DB_ERROR_CANNOT_DROP => 'Tabelle oder Index konnte nicht gelöscht werden', - DB_ERROR_CONSTRAINT => 'Constraint Verletzung', - DB_ERROR_DIVZERO => 'Division durch Null', - DB_ERROR_INVALID => 'ungültig', - DB_ERROR_INVALID_DATE => 'ungültiges Datum oder Zeit', - DB_ERROR_INVALID_NUMBER => 'ungültige Zahl', - DB_ERROR_MISMATCH => 'Unverträglichkeit', - DB_ERROR_NODBSELECTED => 'keine Dantebank ausgewählt', - DB_ERROR_NOSUCHFIELD => 'Feld nicht vorhanden', - DB_ERROR_NOSUCHTABLE => 'Tabelle nicht vorhanden', - DB_ERROR_NOT_CAPABLE => 'Funktion nicht installiert', - DB_ERROR_NOT_FOUND => 'nicht gefunden', - DB_ERROR_NOT_LOCKED => 'nicht gesperrt', - DB_ERROR_SYNTAX => 'Syntaxfehler', - DB_ERROR_UNSUPPORTED => 'nicht Unterstützt', - DB_ERROR_VALUE_COUNT_ON_ROW => 'Anzahl der zurückgelieferten Felder entspricht nicht der Anzahl der Felder in der Abfrage', - DB_ERROR_INVALID_DSN => 'ungültiger DSN', - DB_ERROR_CONNECT_FAILED => 'Verbindung konnte nicht hergestellt werden', - 0 => 'kein Fehler', // DB_OK - DB_ERROR_NEED_MORE_DATA => 'Nicht genügend Daten geliefert', - DB_ERROR_EXTENSION_NOT_FOUND=> 'erweiterung nicht gefunden', - DB_ERROR_NOSUCHDB => 'keine Datenbank', - DB_ERROR_ACCESS_VIOLATION => 'ungenügende Rechte' + 'LANG' => 'de', + DB_ERROR => 'unbekannter Fehler', + DB_ERROR_ALREADY_EXISTS => 'existiert bereits', + DB_ERROR_CANNOT_CREATE => 'kann nicht erstellen', + DB_ERROR_CANNOT_DELETE => 'kann nicht löschen', + DB_ERROR_CANNOT_DROP => 'Tabelle oder Index konnte nicht gelöscht werden', + DB_ERROR_CONSTRAINT => 'Randbedingung verletzt', + DB_ERROR_DIVZERO => 'Division durch Null', + DB_ERROR_INVALID => 'ungültig', + DB_ERROR_INVALID_DATE => 'ungültiges Datum oder Zeit', + DB_ERROR_INVALID_NUMBER => 'ungültige Zahl', + DB_ERROR_MISMATCH => 'Unverträglichkeit', + DB_ERROR_NODBSELECTED => 'Keine Datenbank ausgewählt', + DB_ERROR_NOSUCHFIELD => 'Feld nicht vorhanden', + DB_ERROR_NOSUCHTABLE => 'Tabelle nicht vorhanden', + DB_ERROR_NOT_CAPABLE => 'Funktion nicht installiert', + DB_ERROR_NOT_FOUND => 'nicht gefunden', + DB_ERROR_NOT_LOCKED => 'nicht gesperrt', + DB_ERROR_SYNTAX => 'Syntaxfehler', + DB_ERROR_UNSUPPORTED => 'nicht unterstützt', + DB_ERROR_VALUE_COUNT_ON_ROW => 'Anzahl der zurückgelieferten Felder entspricht nicht der Anzahl der Felder in der Abfrage', + DB_ERROR_INVALID_DSN => 'ungültiger DSN', + DB_ERROR_CONNECT_FAILED => 'Verbindung konnte nicht hergestellt werden', + 0 => 'kein Fehler', // DB_OK + DB_ERROR_NEED_MORE_DATA => 'Nicht genügend Daten geliefert', + DB_ERROR_EXTENSION_NOT_FOUND=> 'Erweiterung nicht gefunden', + DB_ERROR_NOSUCHDB => 'keine Datenbank', + DB_ERROR_ACCESS_VIOLATION => 'ungenügende Rechte' ); diff --git a/lang/adodb-id.inc.php b/lang/adodb-id.inc.php new file mode 100644 index 00000000..f5344c63 --- /dev/null +++ b/lang/adodb-id.inc.php @@ -0,0 +1,36 @@ +<?php +# Indonesian language +# Bambang Riswanto <bamz3r@gmail.com> +$ADODB_LANG_ARRAY = array ( + 'LANG' => 'id', + DB_ERROR => 'kesalahan tidak diketahui', + DB_ERROR_ALREADY_EXISTS => 'sudah ada', + DB_ERROR_CANNOT_CREATE => 'tak dapat membuat', + DB_ERROR_CANNOT_DELETE => 'tak dapat menghapus', + DB_ERROR_CANNOT_DROP => 'tak dapat menghapus', + DB_ERROR_CONSTRAINT => 'pelanggaran kendala', + DB_ERROR_DIVZERO => 'pembagian dengan 0', + DB_ERROR_INVALID => 'tidak sah', + DB_ERROR_INVALID_DATE => 'tanggal atau waktu tidak valid', + DB_ERROR_INVALID_NUMBER => 'nomor tidak sah', + DB_ERROR_MISMATCH => 'tak cocok', + DB_ERROR_NODBSELECTED => 'tak ada database dipilih', + DB_ERROR_NOSUCHFIELD => 'kolom tak ditemukan', + DB_ERROR_NOSUCHTABLE => 'tabel tak ditemukan', + DB_ERROR_NOT_CAPABLE => 'kemampuan DB tak memadai', + DB_ERROR_NOT_FOUND => 'tidak ditemukan', + DB_ERROR_NOT_LOCKED => 'tidak terkunci', + DB_ERROR_SYNTAX => 'kesalahan sintak', + DB_ERROR_UNSUPPORTED => 'tak didukung', + DB_ERROR_VALUE_COUNT_ON_ROW => 'menghitung isi pada baris', + DB_ERROR_INVALID_DSN => 'DSN tidak sah', + DB_ERROR_CONNECT_FAILED => 'koneksi gagal', + 0 => 'tak ada kesalahan', // DB_OK + DB_ERROR_NEED_MORE_DATA => 'data yang dimasukan tidak memadai', + DB_ERROR_EXTENSION_NOT_FOUND=> 'ekstensi tak ditemukan', + DB_ERROR_NOSUCHDB => 'database tak ditemukan', + DB_ERROR_ACCESS_VIOLATION => 'izin tidak memadai', + DB_ERROR_DEADLOCK => 'kebuntuan terdeteksi', + DB_ERROR_STATEMENT_TIMEOUT => 'perintah kehabisan waktu', + DB_ERROR_SERIALIZATION_FAILURE => 'tak dapat melanjutkan akses' +); diff --git a/lang/adodb-oc.inc.php b/lang/adodb-oc.inc.php new file mode 100644 index 00000000..d62b67b3 --- /dev/null +++ b/lang/adodb-oc.inc.php @@ -0,0 +1,31 @@ +<?php +$ADODB_LANG_ARRAY = array ( + 'LANG' => 'oc', + DB_ERROR => 'error desconeguda', + DB_ERROR_ALREADY_EXISTS => 'existÃs ja', + DB_ERROR_CANNOT_CREATE => 'creacion impossibla', + DB_ERROR_CANNOT_DELETE => 'escafament impossible', + DB_ERROR_CANNOT_DROP => 'supression impossibla', + DB_ERROR_CONSTRAINT => 'violacion de constrenta', + DB_ERROR_DIVZERO => 'division per zèro', + DB_ERROR_INVALID => 'invalid', + DB_ERROR_INVALID_DATE => 'data o ora invalida', + DB_ERROR_INVALID_NUMBER => 'nombre invalid', + DB_ERROR_MISMATCH => 'error de concordà ncia', + DB_ERROR_NODBSELECTED => 'pas de basa de donadas de seleccionada', + DB_ERROR_NOSUCHFIELD => 'nom de colomna invalid', + DB_ERROR_NOSUCHTABLE => 'taula o vista inexistenta', + DB_ERROR_NOT_CAPABLE => 'foncion opcionala pas installada', + DB_ERROR_NOT_FOUND => 'pas trobat', + DB_ERROR_NOT_LOCKED => 'pas verrolhat', + DB_ERROR_SYNTAX => 'error de sintaxi', + DB_ERROR_UNSUPPORTED => 'pas suportat', + DB_ERROR_VALUE_COUNT_ON_ROW => 'valor inserida tròp granda per colomna', + DB_ERROR_INVALID_DSN => 'DSN invalid', + DB_ERROR_CONNECT_FAILED => 'fracà s a la connexion', + 0 => "pas d'error", // DB_OK + DB_ERROR_NEED_MORE_DATA => 'donadas provesidas insufisentas', + DB_ERROR_EXTENSION_NOT_FOUND=> 'extension pas trobada', + DB_ERROR_NOSUCHDB => 'basa de donadas desconeguda', + DB_ERROR_ACCESS_VIOLATION => 'dreits insufisents' +); diff --git a/lang/adodb-pt-br.inc.php b/lang/adodb-pt-br.inc.php index 9c687b06..ba67167e 100644 --- a/lang/adodb-pt-br.inc.php +++ b/lang/adodb-pt-br.inc.php @@ -1,6 +1,6 @@ <?php // contributed by "Levi Fukumori" levi _AT_ fukumori _DOT_ com _DOT_ br -// portugese (brazilian) +// portuguese (brazilian) $ADODB_LANG_ARRAY = array ( 'LANG' => 'pt-br', DB_ERROR => 'erro desconhecido', diff --git a/pear/Auth/Container/ADOdb.php b/pear/Auth/Container/ADOdb.php index 1c60b553..e28bcb8d 100644 --- a/pear/Auth/Container/ADOdb.php +++ b/pear/Auth/Container/ADOdb.php @@ -58,7 +58,7 @@ class Auth_Container_ADOdb extends Auth_Container /** * Constructor of the container class * - * Initate connection to the database via PEAR::ADOdb + * Initiate connection to the database via PEAR::ADOdb * * @param string Connection data or DB object * @return object Returns an error object if something went wrong @@ -262,7 +262,7 @@ class Auth_Container_ADOdb extends Auth_Container continue; } // Use reference to the auth object if exists - // This is because the auth session variable can change so a static call to setAuthData does not make sence + // This is because the auth session variable can change so a static call to setAuthData does not make sense if(is_object($this->_auth_obj)){ $this->_auth_obj->setAuthData($key, $value); } else { diff --git a/perf/perf-informix.inc.php b/perf/perf-informix.inc.php index 15eb3128..c6874b07 100644 --- a/perf/perf-informix.inc.php +++ b/perf/perf-informix.inc.php @@ -23,7 +23,7 @@ if (!defined('ADODB_DIR')) die(); // class perf_informix extends adodb_perf{ - // Maximum size on varchar upto 9.30 255 chars + // Maximum size on varchar up to 9.30 255 chars // better truncate varchar to 255 than char(4000) ? var $createTableSQL = "CREATE TABLE adodb_logsql ( created datetime year to second NOT NULL, diff --git a/perf/perf-mysql.inc.php b/perf/perf-mysql.inc.php index 00801f0a..5c3f8764 100644 --- a/perf/perf-mysql.inc.php +++ b/perf/perf-mysql.inc.php @@ -118,6 +118,12 @@ class perf_mysql extends adodb_perf{ return $s; } + /** + * Returns a list of table statuses. + * + * @param string $orderby Unused (compatibility with parent method) + * @return string A formatted set of recordsets + */ function tables($orderby='1') { if (!$this->tablesSQL) return false; @@ -302,11 +308,9 @@ class perf_mysql extends adodb_perf{ case ADODB_OPT_LOW : $sql = $this->optimizeTableLow; break; case ADODB_OPT_HIGH : $sql = $this->optimizeTableHigh; break; default : - { - // May dont use __FUNCTION__ constant for BC (__FUNCTION__ Added in PHP 4.3.0) + // May don't use __FUNCTION__ constant for BC (__FUNCTION__ Added in PHP 4.3.0) ADOConnection::outp( sprintf( "<p>%s: '%s' using of undefined mode '%s'</p>", __CLASS__, __FUNCTION__, $mode)); return false; - } } $sql = sprintf( $sql, $table); diff --git a/perf/perf-postgres.inc.php b/perf/perf-postgres.inc.php index b1fbf980..e5599781 100644 --- a/perf/perf-postgres.inc.php +++ b/perf/perf-postgres.inc.php @@ -113,10 +113,8 @@ class perf_postgres extends adodb_perf{ case ADODB_OPT_LOW : $sql = $this->optimizeTableLow; break; case ADODB_OPT_HIGH: $sql = $this->optimizeTableHigh; break; default : - { ADOConnection::outp(sprintf("<p>%s: '%s' using of undefined mode '%s'</p>", __CLASS__, 'optimizeTable', $mode)); return false; - } } $sql = sprintf($sql, $table); diff --git a/scripts/buildrelease.py b/scripts/buildrelease.py index 0b37b97b..e151b5a2 100755 --- a/scripts/buildrelease.py +++ b/scripts/buildrelease.py @@ -27,7 +27,9 @@ release_branch = "master" release_prefix = "adodb" # Directories and files to exclude from release tarballs +# For consistency, this should match the list in .gitattributes exclude_list = (".git*", + ".mailmap", "replicate", "scripts", "tests", @@ -41,7 +43,7 @@ exclude_list = (".git*", # Command-line options options = "hb:dfk" -long_options = ["help", "branch", "debug", "fresh", "keep"] +long_options = ["help", "branch=", "debug", "fresh", "keep"] # Global flags debug_mode = False @@ -225,7 +227,7 @@ def main(): retry = False continue else: - # We already tried to delete or some other error occured + # We already tried to delete or some other error occurred raise # Create tarballs diff --git a/scripts/fix-static-docs.php b/scripts/fix-static-docs.php new file mode 100644 index 00000000..32133e02 --- /dev/null +++ b/scripts/fix-static-docs.php @@ -0,0 +1,221 @@ +<?php +/** +* A Program to post-process the dokuwiki document export to clean it up +* and fix the broken links +* +* @link http://adodb.org/dokuwiki/doku.php?id=v6:offline_docs_build +* @author Mark Newnham +* @since 02/13/2015 +*/ + +/** +* Recurses a directory and deletes files inside +* +* Copied from php.net +* +* @param string $dir The driectory name +* $return null +*/ +function delTree($dir) { + $files = array_diff(scandir($dir), array('.','..')); + foreach ($files as $file) { + (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file"); + } + return rmdir($dir); +} + +/** +* Initializes the listdiraux method with a starting directory point +* +* copied from php.net +* +* @param string $dir Starting directory +* @return array FQ list of files +*/ +function listdir($dir='.') { + if (!is_dir($dir)) { + return false; + } + $files = array(); + listdiraux($dir, $files); + + return $files; +} + +/** +* Recurses a directory structure and creates a list of files +* +* @param string $dir Starting directory +* @param string[] $files By reference, the current file list +* @return null +*/ +function listdiraux($dir, &$files) { + $handle = opendir($dir); + while (($file = readdir($handle)) !== false) { + if ($file == '.' || $file == '..') { + continue; + } + + if (preg_match('/v6$/',$file)) + /* + * This is only v5 documentation + */ + continue; + + $filepath = $dir == '.' ? $file : $dir . '/' . $file; + if (is_link($filepath)) + continue; + if (is_file($filepath)) + $files[] = $filepath; + else if (is_dir($filepath)) + listdiraux($filepath, $files); + } + closedir($handle); +} +/* +* Clean up the documentation directory from prior use +*/ +if (is_dir('documentation')) + deltree('documentation'); +mkdir('documentation'); + +$files = listdir('documentation-base'); +sort($files, SORT_LOCALE_STRING); + +/* +* Loop through files in documentation-base directory, creating a mirror +* structure in documentation, and applying the post-process rules defined +* below +*/ +foreach ($files as $f) { + + $r = str_replace('documentation-base','documentation',$f); + $dList = explode ('/',$r); + $titleList = $dList; + /* + * Get rid of the initial directory + */ + array_shift($titleList); + + $depth = count($dList) -2; + $dSlash = ''; + while(count($dList) > 1) + { + $dSlash .= array_shift($dList) . '/'; + if (!is_dir($dSlash)) + mkdir($dSlash); + } + if (!is_file($f)) + continue; + if (substr($f,-4) <> 'html') + { + /* + * An image or something else, copy unmodified + */ + copy ($f,$r); + continue; + } + + $prepend = str_repeat('../',$depth); + + $doc = new DOMDocument(); + @$doc->loadHTMLFile($f); + + /* + * Remove Page Tools Group + */ + $xpath = new DOMXPath($doc); + + /* + * Remove Top Menu Tools Group, and add a link to the ADOdb site + */ + $nodes = $xpath->query("//div[@class='tools group']"); + foreach($nodes as $node) { + $pn = $node->parentNode; + $pn->removeChild($node); + $newChild = $doc->createElement('div',''); + $newDiv = $pn->appendChild($newChild); + $newDiv->setAttribute('style','text-align:right'); + $newChild = $doc->createElement('a','ADOdb Web Site'); + $newA = $newDiv->appendChild($newChild); + $newA->setAttribute('href','http://adodb.org'); + } + + /* + * Remove Trace + */ + $nodes = $xpath->query("//div[@class='breadcrumbs']"); + foreach($nodes as $node) { + $node->parentNode->removeChild($node); + } + + /* + * Remove Side Menu Tools Group + */ + $nodes = $xpath->query("//div[@id='dokuwiki__pagetools']"); + foreach($nodes as $node) { + $node->parentNode->removeChild($node); + } + + /* + * Fix main links + */ + $nodes = $xpath->query("//a[@class='wikilink1']"); + foreach($nodes as $node) { + $n = $node->getAttribute('title'); + $p = $prepend . str_replace(':','/',$n) . '.html'; + $node->setAttribute('href', $p); + } + + /* + * Fix In Page links + */ + $nodes = $xpath->query("//a[@class='wikilink2']"); + foreach($nodes as $node) { + $n = $node->getAttribute('title'); + $p = $prepend . str_replace(':','/',$n) . '.html'; + $node->setAttribute('href', $p); + } + + /* + * Make Graphic point to first page. This will break if the image size + * ever changes. + */ + $corePage = $prepend . '/index.html'; + $nodes = $xpath->query("//img[@width='176']"); + foreach($nodes as $node) { + $node->parentNode->setAttribute('href', $corePage); + } + + /* + * Change title of page + */ + $nodes = $xpath->query("//title"); + foreach($nodes as $node) { + + + $docTitle = implode(':',$titleList); + $docTitle = str_replace('.html','',$docTitle); + $pn = $node->parentNode; + $pn->removeChild($node); + $newChild = $doc->createElement('title',$docTitle); + $pn->appendChild($newChild); + + } + + $doc->saveHTMLFile($r); + + echo $r, "\n"; +} +/* +* Now remove the original index and replace it with the hardcopy documentation one +*/ +unlink ('documentation/index.html'); +rename('documentation/adodb_index.html','documentation/index.html'); + +/* +* We could add in an auto zip and upload here, but this is a good place to +* stop and check the output +*/ + +?> diff --git a/scripts/updateversion.py b/scripts/updateversion.py index 0c39fd53..a4450707 100755 --- a/scripts/updateversion.py +++ b/scripts/updateversion.py @@ -261,7 +261,7 @@ def update_changelog(version): ) # Stable release (X.Y.0) - # Replace the 1st occurence of markdown level 2 header matching version + # Replace the 1st occurrence of markdown level 2 header matching version # and release date patterns elif not version_is_patch(version): print " Updating release date for v%s" % version diff --git a/scripts/uploadrelease.py b/scripts/uploadrelease.py index 5b295cbb..9f6f0aee 100755 --- a/scripts/uploadrelease.py +++ b/scripts/uploadrelease.py @@ -5,6 +5,7 @@ from distutils.version import LooseVersion import getopt +import getpass import glob import os from os import path @@ -14,12 +15,15 @@ import sys # Directories and files to exclude from release tarballs +# for debugging, set to a local dir e.g. "localhost:/tmp/sf-adodb/" sf_files = "frs.sourceforge.net:/home/frs/project/adodb/" + +# rsync command template rsync_cmd = "rsync -vP --rsh ssh {opt} {src} {usr}@{dst}" # Command-line options -options = "hn" -long_options = ["help", "dry-run"] +options = "hu:n" +long_options = ["help", "user=", "dry-run"] def usage(): @@ -29,12 +33,12 @@ def usage(): current one if unspecified) to Sourceforge. Parameters: - username Sourceforge user account release_path Location of the release files to upload (see buildrelease.py) Options: -h | --help Show this usage message + -u | --user <name> Sourceforge account (defaults to current user) -n | --dry-run Do not upload the files ''' % ( path.basename(__file__) @@ -53,14 +57,27 @@ def call_rsync(usr, opt, src, dst): command = rsync_cmd.format(usr=usr, opt=opt, src=src, dst=dst) + # Create directory if it does not exist + dst_split = dst.rsplit(':') + host = dst_split[0] + dst = dst_split[1] + mkdir = 'ssh {usr}@{host} mkdir -p {dst}'.format( + usr=usr, + host=host, + dst=dst + ) + if dry_run: + print mkdir print command else: + subprocess.call(mkdir, shell=True) subprocess.call(command, shell=True) def get_release_version(): - ''' Get the version number from the zip file to upload + ''' Returns the version number (X.Y.Z) from the zip file to upload, + excluding the SemVer suffix ''' try: zipfile = glob.glob('adodb-*.zip')[0] @@ -70,7 +87,7 @@ def get_release_version(): try: version = re.search( - "^adodb-([\d]+\.[\d]+\.[\d]+)\.zip$", + "^adodb-([\d]+\.[\d]+\.[\d]+)(-(alpha|beta|rc)\.[\d]+)?\.zip$", zipfile ).group(1) except AttributeError: @@ -82,15 +99,26 @@ def get_release_version(): def sourceforge_target_dir(version): - ''' Returns the sourceforge target directory - Base directory as defined in sf_files global variable, plus + ''' Returns the sourceforge target directory, relative to the root + directory (defined in sf_files global variable): basedir/subdir, with + basedir: + - for ADOdb version 5: adodb-php5-only + - for newer versions: adodbX (where X is the major version number) + subdir: - if version >= 5.21: adodb-X.Y - for older versions: adodb-XYZ-for-php5 ''' - # Keep only X.Y (discard patch number) - short_version = version.rsplit('.', 1)[0] + major_version = int(version.rsplit('.')[0]) + + # Base directory + if major_version == 5: + directory = 'adodb-php5-only/' + else: + directory = 'adodb{}/'.format(major_version) + + # Keep only X.Y (discard patch number and pre-release suffix) + short_version = version.split('-')[0].rsplit('.', 1)[0] - directory = 'adodb-php5-only/' if LooseVersion(version) >= LooseVersion('5.21'): directory += "adodb-" + short_version else: @@ -112,12 +140,9 @@ def process_command_line(): usage() sys.exit(2) - if len(args) < 1: - usage() - print "ERROR: please specify the Sourceforge user and release_path" - sys.exit(1) - # Default values for flags + username = getpass.getuser() + print username dry_run = False for opt, val in opts: @@ -125,15 +150,18 @@ def process_command_line(): usage() sys.exit(0) + elif opt in ("-u", "--user"): + username = val + elif opt in ("-n", "--dry-run"): dry_run = True # Mandatory parameters - username = args[0] + # (none) # Change to release directory, current if not specified try: - release_path = args[1] + release_path = args[0] os.chdir(release_path) except IndexError: release_path = os.getcwd() @@ -20,7 +20,7 @@ * * example: * - * http://localhost/php/server.php?select+*+from+table&nrows=10&offset=2 + * http://localhost/php/server.php?sql=select+*+from+table&nrows=10&offset=2 */ @@ -33,7 +33,7 @@ $ACCEPTIP = '127.0.0.1'; /* * Connection parameters */ -$driver = 'mysql'; +$driver = 'mysqli'; $host = 'localhost'; // DSN for odbc $uid = 'root'; $pwd = 'garbase-it-is'; @@ -80,7 +80,7 @@ if (empty($_REQUEST['sql'])) err('No SQL'); $conn = ADONewConnection($driver); -if (!$conn->Connect($host,$uid,$pwd,$database)) err($conn->ErrorNo(). $sep . $conn->ErrorMsg()); +if (!$conn->connect($host,$uid,$pwd,$database)) err($conn->errorNo(). $sep . $conn->errorMsg()); $sql = undomq($_REQUEST['sql']); if (isset($_REQUEST['fetch'])) @@ -89,12 +89,12 @@ if (isset($_REQUEST['fetch'])) if (isset($_REQUEST['nrows'])) { $nrows = $_REQUEST['nrows']; $offset = isset($_REQUEST['offset']) ? $_REQUEST['offset'] : -1; - $rs = $conn->SelectLimit($sql,$nrows,$offset); + $rs = $conn->selectLimit($sql,$nrows,$offset); } else - $rs = $conn->Execute($sql); + $rs = $conn->execute($sql); if ($rs){ //$rs->timeToLive = 1; echo _rs2serialize($rs,$conn,$sql); - $rs->Close(); + $rs->close(); } else - err($conn->ErrorNo(). $sep .$conn->ErrorMsg()); + err($conn->errorNo(). $sep .$conn->errorMsg()); diff --git a/session/adodb-compress-bzip2.php b/session/adodb-compress-bzip2.php index 5b55e191..3b823eb9 100644 --- a/session/adodb-compress-bzip2.php +++ b/session/adodb-compress-bzip2.php @@ -41,8 +41,8 @@ class ADODB_Compress_Bzip2 { /** */ function setBlockSize($block_size) { - assert('$block_size >= 1'); - assert('$block_size <= 9'); + assert($block_size >= 1); + assert($block_size <= 9); $this->_block_size = (int) $block_size; } @@ -55,8 +55,8 @@ class ADODB_Compress_Bzip2 { /** */ function setWorkLevel($work_level) { - assert('$work_level >= 0'); - assert('$work_level <= 250'); + assert($work_level >= 0); + assert($work_level <= 250); $this->_work_level = (int) $work_level; } @@ -69,7 +69,7 @@ class ADODB_Compress_Bzip2 { /** */ function setMinLength($min_length) { - assert('$min_length >= 0'); + assert($min_length >= 0); $this->_min_length = (int) $min_length; } diff --git a/session/adodb-compress-gzip.php b/session/adodb-compress-gzip.php index 386199b2..ec76e10c 100644 --- a/session/adodb-compress-gzip.php +++ b/session/adodb-compress-gzip.php @@ -38,8 +38,8 @@ class ADODB_Compress_Gzip { /** */ function setLevel($level) { - assert('$level >= 0'); - assert('$level <= 9'); + assert($level >= 0); + assert($level <= 9); $this->_level = (int) $level; } @@ -52,7 +52,7 @@ class ADODB_Compress_Gzip { /** */ function setMinLength($min_length) { - assert('$min_length >= 0'); + assert($min_length >= 0); $this->_min_length = (int) $min_length; } diff --git a/session/adodb-sess.txt b/session/adodb-sess.txt index c6c76858..1e9a8aac 100644 --- a/session/adodb-sess.txt +++ b/session/adodb-sess.txt @@ -58,10 +58,10 @@ Originally, the user had to call adodb_sess_open(). Postgres SAP -* filter(): Used to support multiple, simulataneous encryption/compression +* filter(): Used to support multiple, simultaneous encryption/compression schemes. -* Debug support is improved thru _rsdump() function, which is called after +* Debug support is improved through _rsdump() function, which is called after every database call. ------------ @@ -120,7 +120,7 @@ next release of ADODB. If so, I will modify the current documentation to detail the new functionality. To that end, what file(s) contain the documentation? Please -send them to me if they are not publically available. +send them to me if they are not publicly available. Also, if there is *anything* in the code that you like to see changed, let me know. diff --git a/session/adodb-session.php b/session/adodb-session.php index 51cea7f7..e1a04928 100644 --- a/session/adodb-session.php +++ b/session/adodb-session.php @@ -70,7 +70,7 @@ function adodb_session_regenerate_id() } else { session_id(md5(uniqid(rand(), true))); $ck = session_get_cookie_params(); - setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); + setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure'], $ck['httponly']); //@session_start(); } $new_id = session_id(); @@ -80,7 +80,7 @@ function adodb_session_regenerate_id() if (!$ok) { session_id($old_id); if (empty($ck)) $ck = session_get_cookie_params(); - setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); + setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure'], $ck['httponly']); return false; } @@ -599,7 +599,7 @@ class ADODB_Session { $sql = "SELECT $data FROM $table WHERE sesskey = $binary $qkey AND expiry >= " . time(); /* Lock code does not work as it needs to hold transaction within whole page, and we don't know if - developer has commited elsewhere... :( + developer has committed elsewhere... :( */ #if (ADODB_Session::Lock()) # $rs = $conn->RowLock($table, "$binary sesskey = $qkey AND expiry >= " . time(), $data); @@ -921,12 +921,12 @@ ADODB_Session::_init(); if (empty($ADODB_SESSION_READONLY)) register_shutdown_function('session_write_close'); -// for backwards compatability only +// for backwards compatibility only function adodb_sess_open($save_path, $session_name, $persist = true) { return ADODB_Session::open($save_path, $session_name, $persist); } -// for backwards compatability only +// for backwards compatibility only function adodb_sess_gc($t) { return ADODB_Session::gc($t); diff --git a/session/adodb-session2.php b/session/adodb-session2.php index 77dc1853..a0ce0efa 100644 --- a/session/adodb-session2.php +++ b/session/adodb-session2.php @@ -100,7 +100,7 @@ function adodb_session_regenerate_id() } else { session_id(md5(uniqid(rand(), true))); $ck = session_get_cookie_params(); - setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); + setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure'], $ck['httponly']); //@session_start(); } $new_id = session_id(); @@ -110,7 +110,7 @@ function adodb_session_regenerate_id() if (!$ok) { session_id($old_id); if (empty($ck)) $ck = session_get_cookie_params(); - setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); + setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure'], $ck['httponly']); return false; } @@ -158,7 +158,7 @@ class ADODB_Session { */ static function driver($driver = null) { - static $_driver = 'mysql'; + static $_driver = 'mysqli'; static $set = false; if (!is_null($driver)) { @@ -563,29 +563,44 @@ class ADODB_Session { # assert('$database'); # assert('$driver'); # assert('$host'); - - $conn = ADONewConnection($driver); - - if ($debug) { - $conn->debug = true; - ADOConnection::outp( " driver=$driver user=$user db=$database "); - } - - if (empty($conn->_connectionID)) { // not dsn + if (strpos($driver, 'pdo_') === 0){ + $conn = ADONewConnection('pdo'); + $driver = str_replace('pdo_', '', $driver); + $dsn = $driver.':'.'hostname='.$host.';dbname='.$database.';'; if ($persist) { switch($persist) { default: - case 'P': $ok = $conn->PConnect($host, $user, $password, $database); break; - case 'C': $ok = $conn->Connect($host, $user, $password, $database); break; - case 'N': $ok = $conn->NConnect($host, $user, $password, $database); break; + case 'P': $ok = $conn->PConnect($dsn,$user,$password); break; + case 'C': $ok = $conn->Connect($dsn,$user,$password); break; + case 'N': $ok = $conn->NConnect($dsn,$user,$password); break; } } else { - $ok = $conn->Connect($host, $user, $password, $database); + $ok = $conn->Connect($dsn,$user,$password); + } + }else{ + $conn = ADONewConnection($driver); + if ($debug) { + $conn->debug = true; + ADOConnection::outp( " driver=$driver user=$user db=$database "); + } + + if (empty($conn->_connectionID)) { // not dsn + if ($persist) { + switch($persist) { + default: + case 'P': $ok = $conn->PConnect($host, $user, $password, $database); break; + case 'C': $ok = $conn->Connect($host, $user, $password, $database); break; + case 'N': $ok = $conn->NConnect($host, $user, $password, $database); break; + } + } else { + $ok = $conn->Connect($host, $user, $password, $database); + } + } else { + $ok = true; // $conn->_connectionID is set after call to ADONewConnection } - } else { - $ok = true; // $conn->_connectionID is set after call to ADONewConnection } + if ($ok) $GLOBALS['ADODB_SESS_CONN'] = $conn; else ADOConnection::outp('<p>Session: connection failed</p>', false); @@ -628,7 +643,7 @@ class ADODB_Session { $sql = "SELECT $ADODB_SESSION_SELECT_FIELDS FROM $table WHERE sesskey = $binary ".$conn->Param(0)." AND expiry >= " . $conn->sysTimeStamp; /* Lock code does not work as it needs to hold transaction within whole page, and we don't know if - developer has commited elsewhere... :( + developer has committed elsewhere... :( */ #if (ADODB_Session::Lock()) # $rs = $conn->RowLock($table, "$binary sesskey = $qkey AND expiry >= " . time(), sessdata); @@ -806,7 +821,7 @@ class ADODB_Session { //assert('$table'); $qkey = $conn->quote($key); - $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; + $binary = $conn->dataProvider === 'mysql' || $conn->dataProvider === 'pdo' ? '/*! BINARY */' : ''; if ($expire_notify) { reset($expire_notify); @@ -926,12 +941,12 @@ ADODB_Session::_init(); if (empty($ADODB_SESSION_READONLY)) register_shutdown_function('session_write_close'); -// for backwards compatability only +// for backwards compatibility only function adodb_sess_open($save_path, $session_name, $persist = true) { return ADODB_Session::open($save_path, $session_name, $persist); } -// for backwards compatability only +// for backwards compatibility only function adodb_sess_gc($t) { return ADODB_Session::gc($t); diff --git a/session/old/adodb-cryptsession.php b/session/old/adodb-cryptsession.php index c6246b80..3d7589eb 100644 --- a/session/old/adodb-cryptsession.php +++ b/session/old/adodb-cryptsession.php @@ -197,7 +197,7 @@ $Crypt = new MD5Crypt; ADOConnection::outp( ' -- Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'</p>',false); } else { - // bug in access driver (could be odbc?) means that info is not commited + // bug in access driver (could be odbc?) means that info is not committed // properly unless select statement executed in Win2000 if ($ADODB_SESS_CONN->databaseType == 'access') $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); diff --git a/session/old/adodb-session-clob.php b/session/old/adodb-session-clob.php index e7c00388..8c03ddc3 100644 --- a/session/old/adodb-session-clob.php +++ b/session/old/adodb-session-clob.php @@ -318,7 +318,7 @@ function adodb_sess_write($key, $val) ADOConnection::outp( ' -- Session Replace: '.nl2br($err).'</p>',false); } else { - // bug in access driver (could be odbc?) means that info is not commited + // bug in access driver (could be odbc?) means that info is not committed // properly unless select statement executed in Win2000 if ($ADODB_SESS_CONN->databaseType == 'access') $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); diff --git a/session/old/adodb-session.php b/session/old/adodb-session.php index 548b32e5..60ec0eb8 100644 --- a/session/old/adodb-session.php +++ b/session/old/adodb-session.php @@ -307,7 +307,7 @@ function adodb_sess_write($key, $val) ADOConnection::outp( ' -- Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'</p>',false); } else { - // bug in access driver (could be odbc?) means that info is not commited + // bug in access driver (could be odbc?) means that info is not committed // properly unless select statement executed in Win2000 if ($ADODB_SESS_CONN->databaseType == 'access') $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); diff --git a/session/session_schema.xml b/session/session_schema.xml index 27e47bfe..c1f75316 100644 --- a/session/session_schema.xml +++ b/session/session_schema.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <schema version="0.2"> <table name="sessions"> - <desc>table for ADOdb session-management</desc> + <descr>table for ADOdb session-management</descr> <field name="SESSKEY" type="C" size="32"> <descr>session key</descr> diff --git a/session/session_schema2.xml b/session/session_schema2.xml index f0d3ec94..89cb4f28 100644 --- a/session/session_schema2.xml +++ b/session/session_schema2.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <schema version="0.3"> <table name="sessions2"> - <desc>table for ADOdb session-management</desc> + <descr>table for ADOdb session-management</descr> <field name="SESSKEY" type="C" size="64"> <descr>session key</descr> diff --git a/tests/test-active-record.php b/tests/test-active-record.php index 59471620..8fb02caa 100644 --- a/tests/test-active-record.php +++ b/tests/test-active-record.php @@ -5,10 +5,7 @@ // uncomment the following if you want to test exceptions if (@$_GET['except']) { - if (PHP_VERSION >= 5) { - include('../adodb-exceptions.inc.php'); - echo "<h3>Exceptions included</h3>"; - } + include('../adodb-exceptions.inc.php'); } $db = NewADOConnection('mysql://root@localhost/northwind?persist'); diff --git a/tests/test.php b/tests/test.php index 770c0dc1..177233b6 100644 --- a/tests/test.php +++ b/tests/test.php @@ -28,7 +28,6 @@ function getmicrotime() } -if (PHP_VERSION < 5) include_once('../adodb-pear.inc.php'); //-------------------------------------------------------------------------------------- //define('ADODB_ASSOC_CASE',1); // @@ -40,13 +39,9 @@ function Err($msg) function CheckWS($conn) { -global $ADODB_EXTENSION; - include_once('../session/adodb-session.php'); if (defined('CHECKWSFAIL')){ echo " TESTING $conn ";flush();} - $saved = $ADODB_EXTENSION; $db = ADONewConnection($conn); - $ADODB_EXTENSION = $saved; if (headers_sent()) { print "<p><b>White space detected in adodb-$conn.inc.php or include file...</b></p>"; //die(); @@ -125,12 +120,10 @@ FROM `nuke_stories` `t1`, `nuke_authors` `t2`, `nuke_stories_cat` `t3`, `nuke_to //print $db->UnixTimeStamp('2003-7-22 23:00:00'); $phpv = phpversion(); - if (defined('ADODB_EXTENSION')) $ext = ' Extension '.ADODB_EXTENSION.' installed'; - else $ext = ''; print "<h3>ADODB Version: $ADODB_vers"; print "<p>Host: <i>$db->host</i>"; print "<br>Database: <i>$db->database</i>"; - print "<br>PHP: <i>$phpv $ext</i></h3>"; + print "<br>PHP: <i>$phpv</i></h3>"; flush(); @@ -1352,11 +1345,6 @@ END Adodb; $rs = $db->SelectLimit('select id,firstname,lastname,created,\'The "young man", he said\' from ADOXYZ',10); - if (PHP_VERSION < 5) { - print "<pre>"; - rs2tabout($rs); - print "</pre>"; - } #print " CacheFlush "; #$db->CacheFlush(); @@ -1589,13 +1577,13 @@ END Adodb; else { $name = $db->GetOne("Select firstname from ADOXYZ where id=1"); if (trim($name) != "Carolx") Err("Error: CompleteTrans (2) should have succeeded, returned name=$name"); - else echo "<p> -- Passed StartTrans test2 - commiting</p>"; + else echo "<p> -- Passed StartTrans test2 - committing</p>"; } } flush(); $saved = $db->debug; $db->debug=1; - $cnt = _adodb_getcount($db, 'select * from ADOXYZ where firstname in (select firstname from ADOXYZ)'); + $cnt = _adodb_S($db, 'select * from ADOXYZ where firstname in (select firstname from ADOXYZ)'); echo "<b>Count=</b> $cnt"; $db->debug=$saved; @@ -1651,16 +1639,21 @@ END Adodb; print "<p>Testing Bad Connection</p>"; flush(); - if (true || PHP_VERSION < 5) { - if ($db->dataProvider == 'odbtp') $db->databaseType = 'odbtp'; - $conn = NewADOConnection($db->databaseType); - $conn->raiseErrorFn = 'adodb_test_err'; - if (1) $conn->PConnect('abc','baduser','badpassword'); - if ($TESTERRS == 2) print "raiseErrorFn tests passed<br>"; - else print "<b>raiseErrorFn tests failed ($TESTERRS)</b><br>"; - - flush(); + if ($db->dataProvider == 'odbtp') { + $db->databaseType = 'odbtp'; } + $conn = NewADOConnection($db->databaseType); + $conn->raiseErrorFn = 'adodb_test_err'; + $conn->PConnect('abc','baduser','badpassword'); + if ($TESTERRS == 2) { + print "raiseErrorFn tests passed<br>"; + } + else { + print "<b>raiseErrorFn tests failed ($TESTERRS)</b><br>"; + } + + flush(); + //////////////////////////////////////////////////////////////////// global $nocountrecs; diff --git a/tests/test4.php b/tests/test4.php index e825b18f..02da4833 100644 --- a/tests/test4.php +++ b/tests/test4.php @@ -45,12 +45,11 @@ $conn->PConnect("localhost", "root", "", "test"); // connect to MySQL, testdb #$conn = ADONewConnection('oci8po'); #$conn->Connect('','scott','natsoft'); -if (PHP_VERSION >= 5) { - $connstr = "mysql:dbname=northwind"; - $u = 'root';$p=''; - $conn = ADONewConnection('pdo'); - $conn->Connect($connstr, $u, $p); -} +$connstr = "mysql:dbname=northwind"; +$u = 'root';$p=''; +$conn = ADONewConnection('pdo'); +$conn->Connect($connstr, $u, $p); + //$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; diff --git a/tests/test_mssqlnative.php b/tests/test_mssqlnative.php new file mode 100644 index 00000000..756e2447 --- /dev/null +++ b/tests/test_mssqlnative.php @@ -0,0 +1,131 @@ +<?php + +error_reporting(E_ALL|E_STRICT); + +include('../adodb.inc.php'); +// -------- Internal Trace functions +function Trace($Msg){ + echo "<br>\n".$Msg; +} +function DieTrace($Msg){ + die("<br>\n".$Msg); +} + + +define('ADODB_ASSOC_CASE',0); +$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; +$db = ADONewConnection("mssqlnative"); // create a connection +$db->Connect('127.0.0.1','adodb','natsoft','northwind') or die('Fail'); + +//========================== +// This code tests GenId +//========================== +function TestGenID() { + global $db; + $PrevDebug=$db->debug; $db->debug=false; // Hide debug if present as Drop can cause errors + $db->Execute("drop sequence MySequence1; +drop sequence MySequence2; +drop sequence MySequence3; +drop table MySequence1Emul; +drop table MySequence2Emul; +drop table MySequence3Emul; +"); + $db->debug=$PrevDebug; // Restore debug Initial State + + $ID1a=$db->GenID("MySequence1"); + $ID2a=$db->GenID("MySequence2"); + $ID1b=$db->GenID("MySequence1"); + $ID2b=$db->GenID("MySequence2"); + Trace("ID1a=$ID1a,ID1b=$ID1b, ID2a=$ID2a,ID2b=$ID2b"); + if(intval($ID1a)+1!==intval($ID1b)) DieTrace(sprintf("ERROR : Second value obtains by MySequence1 should be %d but is %d",$ID1a+1,$ID1b)); + + $db->CreateSequence("MySequence3",100); + $ID2b=$db->GenID("MySequence3"); + if(intval($ID2b)!==100) DieTrace(sprintf("ERROR : Value from MySequence3 should be 100 but is %d",$ID2b)); + + $db->mssql_version=10; // Force to simulate Pre 2012 (without sequence) behavior + $ID1a=$db->GenID("MySequence1Emul"); + $ID2a=$db->GenID("MySequence2Emul"); + $ID1b=$db->GenID("MySequence1Emul"); + $ID2b=$db->GenID("MySequence2Emul"); + echo "ID1a=$ID1a,ID1b=$ID1b, ID2a=$ID2a,ID2b=$ID2b <br>\n"; + if(intval($ID1a+1)!==intval($ID1b)) DieTrace(sprintf("ERROR : Second value obtains by MySequence1Emul should be %d but is %d",$ID1a+1,$ID1b)); + + $db->CreateSequence("MySequence3Emul",100); + $ID2b=$db->GenID("MySequence3Emul"); + if(intval($ID2b)!==100) DieTrace(sprintf("ERROR : Value from MySequence3Emul should be 100 but is %d",$ID2b)); + } //TestGenID() + +//========================== +// This code tests SQLDate +//========================== +function TestSQLDate() +{ + global $db; + $res = $db->GetRow("select testdate," + . $db->SQLDate("d/m/Y", "testdate") . " FR4," + . $db->SQLDate("d/m/y", "testdate") . " FR4b," + . $db->SQLDate("d/m/Y", "NULL") . " nullFR4," + . $db->SQLDate("m/d/Y", "testdate") . " US4," + . $db->SQLDate("m/d/y", "testdate") . " US4b," + . $db->SQLDate("m-d-Y", "testdate") . " USD4," + . $db->SQLDate("m-d-y", "testdate") . " USD4b," + . $db->SQLDate("Y.m.d", "testdate") . " ANSI4," + . $db->SQLDate("d.m.Y", "testdate") . " GE4," + . $db->SQLDate("d.m.y", "testdate") . " GE4b," + . $db->SQLDate("d-m-Y", "testdate") . " IT4," + . $db->SQLDate("d-m-y", "testdate") . " IT4b," + . $db->SQLDate("Y/m/d", "testdate") . " Japan4," + . $db->SQLDate("y/m/d", "testdate") . " Japan4b," + . $db->SQLDate("H:i:s", "testdate") . " timeonly," + . $db->SQLDate("d m Y", "testdate") . " Space4," // Is done by former method + . $db->SQLDate("d m Y", "NULL") . " nullSpace4," + . $db->SQLDate("m-d-Y", "testdatesmall") . " nowUSdash4," + . "null from (select convert(datetime,'2016-12-17 18:55:30.590' ,121) testdate, + convert(datetime,'2016-01-01 18:55:30.590' ,121) testdatesmall,null nulldate) q " + ); + $TestRes=array( + "fr4"=>"17/12/2016", + "fr4b"=>"17/12/2016", + "nullfr4"=>null, + "us4"=>"12/17/2016", + "us4b"=>"12/17/2016", + "ansi4"=>"2016.12.17", + "ge4"=>"17.12.2016", + "ge4b"=>"17.12.2016", + "it4"=>"17-12-2016", + "it4b"=>"17-12-2016", + "japan4"=>"2016/12/17", + "japan4b"=>"2016/12/17", + "space4"=>"17 12 2016", + "nullspace4"=>null, + "timeonly"=>"18:55:30", + ); + var_dump($res); + foreach($TestRes as $k=>$v) + if($v!==$res[$k]) + DieTrace(sprintf("ERROR : Expected for '%s' is '%s', but got '%s'",$k,$v,$res[$k])); +} //TestSQLDate() + +//========================== +// This code is the tests RUNNER +//========================== +$db->debug=true; +// $ToTest Contains * or the name of the test function to RUN +$ToTest="*"; +//$ToTest="TestSQLDate"; + +// Here the generic test runner, will launch all functions of the current file beginning by "test", should not be changed use $ToTest +$functions = get_defined_functions(); +$functions = $functions['user']; +foreach( $functions as $f) { + $refFunc = new ReflectionFunction($f); + if(($refFunc->getFileName()==__FILE__)&&(substr($f,0,4)=='test')) + if(($ToTest=='*')||(strtolower($ToTest)==$f)) + { + Trace("<b>-------- Launch Test : $f ------------------</b>"); + $f(); + } +} + +Trace("<b>=========== End of tests Without Error. ===================</b>");
\ No newline at end of file diff --git a/tests/testdatabases.inc.php b/tests/testdatabases.inc.php index 5df07011..f73b8fe4 100644 --- a/tests/testdatabases.inc.php +++ b/tests/testdatabases.inc.php @@ -275,8 +275,7 @@ if (!empty($testvfp)) { // ODBC if (!empty($testmysql)) { // MYSQL - if (PHP_VERSION >= 5 || $_SERVER['HTTP_HOST'] == 'localhost') $server = 'localhost'; - else $server = "mangrove"; + $server = 'localhost'; $user = 'root'; $password = ''; $database = 'northwind'; $db = ADONewConnection("mysqlt://$user:$password@$server/$database?persist"); print "<h1>Connecting $db->databaseType...</h1>"; @@ -294,8 +293,7 @@ if (!empty($testmysqli)) { // MYSQL $db = ADONewConnection('mysqli'); print "<h1>Connecting $db->databaseType...</h1>"; - if (PHP_VERSION >= 5 || $_SERVER['HTTP_HOST'] == 'localhost') $server = 'localhost'; - else $server = "mangrove"; + $server = 'localhost'; if ($db->PConnect($server, "root", "", "northwind")) { //$db->debug=1;$db->Execute('drop table ADOXYZ'); testdb($db, @@ -310,8 +308,7 @@ if (!empty($testmysqlodbc)) { // MYSQL $db = ADONewConnection('odbc'); $db->hasTransactions = false; print "<h1>Connecting $db->databaseType...</h1>"; - if ($_SERVER['HTTP_HOST'] == 'localhost') $server = 'localhost'; - else $server = "mangrove"; + $server = 'localhost'; if ($db->PConnect('mysql', "root", "")) testdb($db, "create table ADOXYZ (id int, firstname char(24), lastname char(24), created date) type=innodb"); @@ -321,7 +318,7 @@ if (!empty($testmysqlodbc)) { // MYSQL if (!empty($testproxy)){ $db = ADONewConnection('proxy'); print "<h1>Connecting $db->databaseType...</h1>"; - if ($_SERVER['HTTP_HOST'] == 'localhost') $server = 'localhost'; + $server = 'localhost'; if ($db->PConnect('http://localhost/php/phplens/adodb/server.php')) testdb($db, @@ -358,25 +355,15 @@ if (false && !empty($testoracle)) { ADOLoadCode("odbc_db2"); // no longer supported if (!empty($testdb2)) { - if (PHP_VERSION>=5.1) { - $db = ADONewConnection("db2"); - print "<h1>Connecting $db->databaseType...</h1>"; + $db = ADONewConnection("db2"); + print "<h1>Connecting $db->databaseType...</h1>"; - #$db->curMode = SQL_CUR_USE_ODBC; - #$dsn = "driver={IBM db2 odbc DRIVER};Database=test;hostname=localhost;port=50000;protocol=TCPIP; uid=natsoft; pwd=guest"; - if ($db->Connect('localhost','natsoft','guest','test')) { - testdb($db,"create table ADOXYZ (id int, firstname varchar(24), lastname varchar(24),created date)"); - } else print "ERROR: DB2 test requires an server setup with odbc data source db2_sample".'<BR>'.$db->ErrorMsg(); + #$db->curMode = SQL_CUR_USE_ODBC; + #$dsn = "driver={IBM db2 odbc DRIVER};Database=test;hostname=localhost;port=50000;protocol=TCPIP; uid=natsoft; pwd=guest"; + if ($db->Connect('localhost','natsoft','guest','test')) { + testdb($db,"create table ADOXYZ (id int, firstname varchar(24), lastname varchar(24),created date)"); } else { - $db = ADONewConnection("odbc_db2"); - print "<h1>Connecting $db->databaseType...</h1>"; - - $dsn = "db2test"; - #$db->curMode = SQL_CUR_USE_ODBC; - #$dsn = "driver={IBM db2 odbc DRIVER};Database=test;hostname=localhost;port=50000;protocol=TCPIP; uid=natsoft; pwd=guest"; - if ($db->Connect($dsn)) { - testdb($db,"create table ADOXYZ (id int, firstname varchar(24), lastname varchar(24),created date)"); - } else print "ERROR: DB2 test requires an server setup with odbc data source db2_sample".'<BR>'.$db->ErrorMsg(); + print "ERROR: DB2 test requires an server setup with odbc data source db2_sample".'<BR>'.$db->ErrorMsg(); } echo "<hr />"; flush(); @@ -432,7 +419,7 @@ if (!empty($testmssql)) { // MS SQL Server via ODBC } ADOLoadCode("ado_mssql"); -if (!empty($testmssql) && !empty($testado) ) { // ADO ACCESS MSSQL -- thru ODBC -- DSN-less +if (!empty($testmssql) && !empty($testado) ) { // ADO ACCESS MSSQL -- through ODBC -- DSN-less $db = ADONewConnection("ado_mssql"); //$db->debug=1; diff --git a/xmlschema03.dtd b/xmlschema03.dtd index 97850bc7..351ea44b 100644 --- a/xmlschema03.dtd +++ b/xmlschema03.dtd @@ -1,17 +1,22 @@ -<?xml version="1.0"?> -<!DOCTYPE adodb_schema [ <!ELEMENT schema (table*, sql*)> <!ATTLIST schema version CDATA #REQUIRED> <!ELEMENT table (descr?, (field+|DROP), constraint*, opt*, index*, data*)> -<!ATTLIST table name CDATA #REQUIRED platform CDATA #IMPLIED version CDATA #IMPLIED> +<!ATTLIST table + name CDATA #REQUIRED + platform CDATA #IMPLIED + version CDATA #IMPLIED> <!ELEMENT field (descr?, (NOTNULL|KEY|PRIMARY)?, (AUTO|AUTOINCREMENT)?, (DEFAULT|DEFDATE|DEFTIMESTAMP)?, NOQUOTE?, UNSIGNED?, constraint*, opt*)> -<!ATTLIST field name CDATA #REQUIRED type (C|C2|X|X2|B|D|T|L|I|F|N) #REQUIRED size CDATA #IMPLIED opts CDATA #IMPLIED> +<!ATTLIST + field name CDATA #REQUIRED + type (C|C2|X|X2|XL|B|D|T|L|I|I1|I2|I4|I8|F|N) #REQUIRED + size CDATA #IMPLIED + opts CDATA #IMPLIED> <!ELEMENT data (descr?, row+)> <!ATTLIST data platform CDATA #IMPLIED> <!ELEMENT row (f+)> -<!ELEMENT f (#CDATA)> +<!ELEMENT f (#PCDATA)> <!ATTLIST f name CDATA #IMPLIED> -<!ELEMENT descr (#CDATA)> +<!ELEMENT descr (#PCDATA)> <!ELEMENT NOTNULL EMPTY> <!ELEMENT KEY EMPTY> <!ELEMENT PRIMARY EMPTY> @@ -24,20 +29,25 @@ <!ELEMENT NOQUOTE EMPTY> <!ELEMENT UNSIGNED EMPTY> <!ELEMENT DROP EMPTY> -<!ELEMENT constraint (#CDATA)> +<!ELEMENT constraint (#PCDATA)> <!ATTLIST constraint platform CDATA #IMPLIED> -<!ELEMENT opt (#CDATA)> +<!ELEMENT opt (#PCDATA)> <!ATTLIST opt platform CDATA #IMPLIED> <!ELEMENT index ((col+|DROP), CLUSTERED?, BITMAP?, UNIQUE?, FULLTEXT?, HASH?, descr?)> -<!ATTLIST index name CDATA #REQUIRED platform CDATA #IMPLIED> -<!ELEMENT col (#CDATA)> +<!ATTLIST index + name CDATA #REQUIRED + platform CDATA #IMPLIED> +<!ELEMENT col (#PCDATA)> <!ELEMENT CLUSTERED EMPTY> <!ELEMENT BITMAP EMPTY> <!ELEMENT UNIQUE EMPTY> <!ELEMENT FULLTEXT EMPTY> <!ELEMENT HASH EMPTY> <!ELEMENT sql (query+, descr?)> -<!ATTLIST sql name CDATA #IMPLIED platform CDATA #IMPLIED, key CDATA, prefixmethod (AUTO|MANUAL|NONE)> -<!ELEMENT query (#CDATA)> +<!ATTLIST sql + name CDATA #IMPLIED + platform CDATA #IMPLIED + key CDATA #IMPLIED + prefixmethod (AUTO|MANUAL|NONE) #IMPLIED> +<!ELEMENT query (#PCDATA)> <!ATTLIST query platform CDATA #IMPLIED> -]>
\ No newline at end of file |
