diff options
Diffstat (limited to 'drivers')
32 files changed, 3179 insertions, 2558 deletions
diff --git a/drivers/adodb-ado.inc.php b/drivers/adodb-ado.inc.php index b0979ff5..aa8a6197 100644 --- a/drivers/adodb-ado.inc.php +++ b/drivers/adodb-ado.inc.php @@ -122,7 +122,7 @@ class ADODB_ado extends ADOConnection { adSchemaConstraintColumnUsage = 6, adSchemaConstraintTableUsage = 7, adSchemaKeyColumnUsage = 8, - adSchemaReferentialContraints = 9, + adSchemaReferentialConstraints = 9, adSchemaTableConstraints = 10, adSchemaColumnsDomainUsage = 11, adSchemaIndexes = 12, @@ -345,16 +345,6 @@ class ADORecordSet_ado extends ADORecordSet { var $canSeek = true; var $hideErrors = true; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($id); - } - // returns the field object function FetchField($fieldOffset = -1) { @@ -584,7 +574,7 @@ class ADORecordSet_ado extends ADORecordSet { $val=(float) variant_cast($f->value,VT_R8)*3600*24-2209161600; else $val = $f->value; - $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 133:// A date value (yyyymmdd) @@ -599,8 +589,8 @@ class ADORecordSet_ado extends ADORecordSet { if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value); else $val = $f->value; - if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val); - else $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + if (($val % 86400) == 0) $this->fields[] = date('Y-m-d',$val); + else $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 1: // null diff --git a/drivers/adodb-ado5.inc.php b/drivers/adodb-ado5.inc.php index e4fa0cfe..76b85335 100644 --- a/drivers/adodb-ado5.inc.php +++ b/drivers/adodb-ado5.inc.php @@ -147,7 +147,7 @@ class ADODB_ado extends ADOConnection { adSchemaConstraintColumnUsage = 6, adSchemaConstraintTableUsage = 7, adSchemaKeyColumnUsage = 8, - adSchemaReferentialContraints = 9, + adSchemaReferentialConstraints = 9, adSchemaTableConstraints = 10, adSchemaColumnsDomainUsage = 11, adSchemaIndexes = 12, @@ -382,16 +382,6 @@ class ADORecordSet_ado extends ADORecordSet { var $canSeek = true; var $hideErrors = true; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($id); - } - // returns the field object function FetchField($fieldOffset = -1) { @@ -631,7 +621,7 @@ class ADORecordSet_ado extends ADORecordSet { $val= (float) variant_cast($f->value,VT_R8)*3600*24-2209161600; else $val = $f->value; - $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 133:// A date value (yyyymmdd) @@ -646,8 +636,8 @@ class ADORecordSet_ado extends ADORecordSet { if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value); else $val = $f->value; - if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val); - else $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + if (($val % 86400) == 0) $this->fields[] = date('Y-m-d',$val); + else $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 1: // null diff --git a/drivers/adodb-ads.inc.php b/drivers/adodb-ads.inc.php index 436a2d62..5362123f 100644 --- a/drivers/adodb-ads.inc.php +++ b/drivers/adodb-ads.inc.php @@ -287,7 +287,7 @@ class ADODB_ads extends ADOConnection // Returns tables,Views or both on successful execution. Returns // tables by default on successful execution. - function &MetaTables($ttype = false, $showSchema = false, $mask = false) + function MetaTables($ttype = false, $showSchema = false, $mask = false) { $recordSet1 = $this->Execute("select * from system.tables"); if (!$recordSet1) { @@ -325,7 +325,7 @@ class ADODB_ads extends ADOConnection } - function &MetaPrimaryKeys($table, $owner = false) + function MetaPrimaryKeys($table, $owner = false) { $recordSet = $this->Execute("select table_primary_key from system.tables where name='$table'"); if (!$recordSet) { @@ -533,7 +533,7 @@ class ADODB_ads extends ADOConnection } // Returns an array of columns names for a given table - function &MetaColumnNames($table, $numIndexes = false, $useattnum = false) + function MetaColumnNames($table, $numIndexes = false, $useattnum = false) { $recordSet = $this->Execute("select name from system.columns where parent='$table'"); if (!$recordSet) { @@ -684,20 +684,13 @@ class ADORecordSet_ads extends ADORecordSet var $dataProvider = "ads"; var $useFetchArray; - function __construct($id, $mode = false) + function __construct($queryID, $mode = false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $id; + parent::__construct($queryID, $mode); // the following is required for mysql odbc driver in 4.3.1 -- why? $this->EOF = false; $this->_currentRow = -1; - //parent::__construct($id); } @@ -760,7 +753,7 @@ class ADORecordSet_ads extends ADORecordSet function &GetArrayLimit($nrows, $offset = -1) { if ($offset <= 0) { - $rs =& $this->GetArray($nrows); + $rs = $this->GetArray($nrows); return $rs; } $savem = $this->fetchMode; @@ -769,7 +762,7 @@ class ADORecordSet_ads extends ADORecordSet $this->fetchMode = $savem; if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields =& $this->GetRowAssoc(); + $this->fields = $this->GetRowAssoc(); } $results = array(); @@ -802,7 +795,7 @@ class ADORecordSet_ads extends ADORecordSet $rez = @ads_fetch_into($this->_queryID, $this->fields); if ($rez) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields =& $this->GetRowAssoc(); + $this->fields = $this->GetRowAssoc(); } return true; } diff --git a/drivers/adodb-db2.inc.php b/drivers/adodb-db2.inc.php index 77466db1..c5f9992a 100644 --- a/drivers/adodb-db2.inc.php +++ b/drivers/adodb-db2.inc.php @@ -11,7 +11,7 @@ * 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 + * @link https://pecl.php.net/package/ibm_db2 PECL Extension For DB2 * * This file is part of ADOdb, a Database Abstraction Layer library for PHP. * @@ -38,993 +38,1168 @@ define("_ADODB_DB2_LAYER", 2 ); class ADODB_db2 extends ADOConnection { - var $databaseType = "db2"; - var $fmtDate = "'Y-m-d'"; - var $concat_operator = '||'; + var $databaseType = "db2"; + var $fmtDate = "'Y-m-d'"; + var $concat_operator = '||'; - var $sysTime = 'CURRENT TIME'; - var $sysDate = 'CURRENT DATE'; - var $sysTimeStamp = 'CURRENT TIMESTAMP'; + var $sysTime = 'CURRENT TIME'; + var $sysDate = 'CURRENT DATE'; + var $sysTimeStamp = 'CURRENT TIMESTAMP'; - var $fmtTimeStamp = "'Y-m-d H:i:s'"; - var $replaceQuote = "''"; // string to use to replace quotes - var $dataProvider = "db2"; - var $hasAffectedRows = true; + var $fmtTimeStamp = "'Y-m-d H:i:s'"; + var $replaceQuote = "''"; // string to use to replace quotes + var $dataProvider = "db2"; + var $hasAffectedRows = true; - var $binmode = DB2_BINARY; + var $binmode = DB2_BINARY; - /* - * 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 INCREMENT BY 1 NO CACHE - "; - var $_dropSeqSQL = "DROP SEQUENCE %s"; - var $_autocommit = true; - var $_lastAffectedRows = 0; - var $hasInsertID = true; - var $hasGenID = true; + /* + * 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"; - /* - * 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 = '"'; + /** + * SQL to create a new sequence + * + * @var string + */ + var $_genSeqSQL = " + CREATE SEQUENCE %s START WITH %s + NO MAXVALUE NO CYCLE INCREMENT BY 1 NO CACHE + "; - /* - * Holds information about the stored procedure request - * currently being built - */ - private $storedProcedureParameters = false; + /** + * SQL to drop a sequence + * + * @var string + */ + var $_dropSeqSQL = "DROP SEQUENCE %s"; + var $_autocommit = true; + var $_lastAffectedRows = 0; + 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 = '"'; - function __construct() {} + /* + * Holds information about the stored procedure request + * currently being built + */ + private $storedProcedureParameters = false; - protected function _insertID($table = '', $column = '') - { - return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()'); - } - public function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename); - } + /** + * Supports older DB2 versions without record counts + * + * @var boolean + */ + public bool $useLegacyRecordCounting = false; - public function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename,true); - } + /** + * Supports older DB2 versions without 'OFFSET' support + * + * @var boolean + */ + public bool $useLegacyRowOffsets = false; - private function doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persistent=false) - { + function __construct() {} - if (!function_exists('db2_connect')) { - ADOConnection::outp("DB2 extension not installed."); - return null; - } + protected function _insertID($table = '', $column = '') + { + return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()'); + } - $connectionParameters = $this->unpackParameters( - $argDSN, - $argUsername, - $argPassword, - $argDatabasename - ); + public function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename); + } - if ($connectionParameters == null) - { - /* - * Error thrown - */ - return null; - } + public function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename,true); + } - $argDSN = $connectionParameters['dsn']; - $argUsername = $connectionParameters['uid']; - $argPassword = $connectionParameters['pwd']; - $argDatabasename = $connectionParameters['database']; - $useCataloguedConnection = $connectionParameters['catalogue']; + private function doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persistent=false) + { - if ($this->debug){ - if (strcmp($argDSN,'*LOCAL') == 0) - { - $connectMessage = '*LOCAL connection'; - } - else 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); - } + if (!function_exists('db2_connect')) { + ADOConnection::outp("DB2 extension not installed."); + return null; + } - /* - * This needs to be set before the connect(). - */ - ini_set('ibm_db2.binmode', $this->binmode); + $connectionParameters = $this->unpackParameters( + $argDSN, + $argUsername, + $argPassword, + $argDatabasename + ); - if ($persistent) - $db2Function = 'db2_pconnect'; - else - $db2Function = 'db2_connect'; + if ($connectionParameters == null) + { + /* + * Error thrown + */ + return null; + } - /* - * We need to flatten out the connectionParameters - */ + $argDSN = $connectionParameters['dsn']; + $argUsername = $connectionParameters['uid']; + $argPassword = $connectionParameters['pwd']; + $argDatabasename = $connectionParameters['database']; + $useCataloguedConnection = $connectionParameters['catalogue']; - $db2Options = array(); - if ($this->connectionParameters) - { - foreach($this->connectionParameters as $p) - foreach($p as $k=>$v) - $db2Options[$k] = $v; - } + if ($this->debug){ + if (strcmp($argDSN,'*LOCAL') == 0) + { + $connectMessage = '*LOCAL connection'; + } + else 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); + } - if ($useCataloguedConnection) - { - $this->_connectionID = $db2Function($argDatabasename, - $argUsername, - $argPassword, - $db2Options); - } - else + /* + * This needs to be set before the connect(). + */ + ini_set('ibm_db2.binmode', $this->binmode); - $this->_connectionID = $db2Function($argDSN, - '', - '', - $db2Options); + if ($persistent) + $db2Function = 'db2_pconnect'; + else + $db2Function = 'db2_connect'; + /* + * We need to flatten out the connectionParameters + */ - $this->_errorMsg = @db2_conn_errormsg(); + $db2Options = array(); + if ($this->connectionParameters) + { + foreach($this->connectionParameters as $p) + foreach($p as $k=>$v) + $db2Options[$k] = $v; + } - if ($this->_connectionID && $this->connectStmt) - $this->execute($this->connectStmt); + if ($useCataloguedConnection) + { + $this->_connectionID = $db2Function($argDatabasename, + $argUsername, + $argPassword, + $db2Options); + } + else - if ($this->_connectionID && $argDatabasename) - $this->execute("SET SCHEMA=$argDatabasename"); + $this->_connectionID = $db2Function($argDSN, + '', + '', + $db2Options); - return $this->_connectionID != false; - } + $this->_errorMsg = @db2_conn_errormsg(); - /** - * 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 ($this->_connectionID && $this->connectStmt) + $this->execute($this->connectStmt); + if ($this->_connectionID && $argDatabasename) + $this->execute("SET SCHEMA=$argDatabasename"); - $connectionParameters = array( - 'dsn'=>'', - 'uid'=>'', - 'pwd'=>'', - 'database'=>'', - 'catalogue'=>true - ); + return $this->_connectionID != false; - /* - * Shortcut for *LOCAL - */ - if (strcmp($argDSN,'*LOCAL') == 0) - { - $connectionParameters['dsn'] = $argDSN; - $connectionParameters['database'] = $argDatabasename; - $connectionParameters['catalogue'] = false; + } - return $connectionParameters; - } + /** + * 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) + { - /* - * 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; - } + $connectionParameters = array( + 'dsn'=>'', + 'uid'=>'', + 'pwd'=>'', + 'database'=>'', + 'catalogue'=>true + ); - $useCataloguedConnection = true; - $schemaName = ''; + /* + * Shortcut for *LOCAL + */ + if (strcmp($argDSN,'*LOCAL') == 0) + { + $connectionParameters['dsn'] = $argDSN; + $connectionParameters['database'] = $argDatabasename; + $connectionParameters['catalogue'] = false; - 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); + return $connectionParameters; + } - } - $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'; + /* + * 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 ($this->debug) - ADOConnection::outp($errorMessage); - return null; - } - } + if (!$argDSN && !$argDatabasename) + { + $errorMessage = 'Supply either catalogued or uncatalogued connection parameters'; + $this->_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; - } + $useCataloguedConnection = true; + $schemaName = ''; - if ($this->isDsn($argDSN)) - { + 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); - 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'; + } + $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'; - if ($this->debug) - ADOConnection::outp($errorMessage); - return null; - } + 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; + if (!$this->isDsn($argDSN) && $this->isDsn($argDatabasename)) + { + /* + * Switch them around for next test + */ + $temp = $argDSN; + $argDsn = $argDatabasename; + $argDatabasenME = $temp; + } - } - elseif ($argDatabasename) - { - $this->database = $argDatabasename; - $argDSN .= ';database=' . $argDatabasename; - $argDatabasename = ''; - $useCataloguedConnection = false; + if ($this->isDsn($argDSN)) + { - } - else - { - $errorMessage = 'Uncatalogued connection parameters '; - $errorMessage.= 'must contain a database= argument'; + 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'; - if ($this->debug) - ADOConnection::outp($errorMessage); - return null; - } - } + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; + } - if ($argDSN && !$argDatabasename && $useCataloguedConnection) - { - $argDatabasename = $argDSN; - $argDSN = ''; - } + 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->database = $argDatabasename; + $argDSN .= ';database=' . $argDatabasename; + $argDatabasename = ''; + $useCataloguedConnection = false; - if ($useCataloguedConnection - && (!$argDatabasename - || !$argUsername - || !$argPassword)) - { + } + else + { + $errorMessage = 'Uncatalogued connection parameters '; + $errorMessage.= 'must contain a database= argument'; - $errorMessage = 'For catalogued connections, provide '; - $errorMessage.= 'database, username and password'; - $this->_errorMsg = $errorMessage; - if ($this->debug) - ADOConnection::outp($errorMessage); - return null; + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; + } + } - } + if ($argDSN && !$argDatabasename && $useCataloguedConnection) + { + $argDatabasename = $argDSN; + $argDSN = ''; + } - if ($argDatabasename) - $this->database = $argDatabasename; - elseif (!$this->database) - $this->database = $this->getDatabasenameFromDsn($argDSN); + if ($useCataloguedConnection + && (!$argDatabasename + || !$argUsername + || !$argPassword)) + { - $connectionParameters = array('dsn'=>$argDSN, - 'uid'=>$argUsername, - 'pwd'=>$argPassword, - 'database'=>$argDatabasename, - 'catalogue'=>$useCataloguedConnection - ); + $errorMessage = 'For catalogued connections, provide '; + $errorMessage.= 'database, username and password'; + $this->_errorMsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return null; - return $connectionParameters; + } - } + if ($argDatabasename) + $this->database = $argDatabasename; + elseif (!$this->database) + $this->database = $this->getDatabasenameFromDsn($argDSN); - /** - * 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; - } + $connectionParameters = array('dsn'=>$argDSN, + 'uid'=>$argUsername, + 'pwd'=>$argPassword, + 'database'=>$argDatabasename, + 'catalogue'=>$useCataloguedConnection + ); - /** - * Gets the database name from the DSN - * - * @param string $dsnString - * - * @return string - */ - private function getDatabasenameFromDsn($dsnString){ + return $connectionParameters; - $dsnArray = preg_split('/[;=]+/',$dsnString); - $dbIndex = array_search('database',$dsnArray); + } - return $dsnArray[$dbIndex + 1]; - } + /** + * 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 - * - * @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); - return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')"; - } + /** + * Gets the database name from the DSN + * + * @param string $dsnString + * + * @return string + */ + private function getDatabasenameFromDsn($dsnString){ - /** - * 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) - { - if (!$col) $col = $this->sysDate; + $dsnArray = preg_split('/[;=]+/',$dsnString); + $dbIndex = array_search('database',$dsnArray); - /* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */ - if ($fmt== 'Y-m-d H:i:s') - return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')"; + return $dsnArray[$dbIndex + 1]; + } - $s = ''; - $len = strlen($fmt); - for ($i=0; $i < $len; $i++) { - if ($s) $s .= $this->concat_operator; - $ch = $fmt[$i]; - switch($ch) { - case 'Y': - case 'y': - if ($len==1) return "year($col)"; - $s .= "char(year($col))"; - break; - case 'M': - if ($len==1) return "monthname($col)"; - $s .= "substr(monthname($col),1,3)"; - break; - case 'm': - if ($len==1) return "month($col)"; - $s .= "right(digits(month($col)),2)"; - break; - case 'D': - case 'd': - if ($len==1) return "day($col)"; - $s .= "right(digits(day($col)),2)"; - break; - case 'H': - case 'h': - if ($len==1) return "hour($col)"; - if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)"; - else $s .= "''"; - break; - case 'i': - case 'I': - if ($len==1) return "minute($col)"; - if ($col != $this->sysDate) - $s .= "right(digits(minute($col)),2)"; - else $s .= "''"; - break; - case 'S': - case 's': - if ($len==1) return "second($col)"; - if ($col != $this->sysDate) - $s .= "right(digits(second($col)),2)"; - else $s .= "''"; - break; - default: - if ($ch == '\\') { - $i++; - $ch = substr($fmt,$i,1); - } - $s .= $this->qstr($ch); - } - } - return $s; - } + /** + * 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); + return 'TO_DATE('.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 + * + * @param string $fmt The format string e.g. 'm/d/Y' + * @param string $col optionally either a column name,an ISO date string or a + * ate geerating DB2 function like CURRENT DATE + * + * @return string + */ + public function sqlDate($fmt, $col=false) + { + $usesSysDate = false; + $trailer = ''; + $dCol = ''; + $tCol = ''; + if (!$col) { + /* + * If no column is provided, we use the system date or time + */ - function serverInfo() - { - $sql = "SELECT service_level, fixpack_num - FROM TABLE(sysproc.env_get_inst_info()) - AS INSTANCEINFO"; - $row = $this->GetRow($sql); + $dCol = $this->sysDate; + $tCol = $this->sysTime; + /* + * Anything not from a column needs this + */ + $trailer = 'FROM SYSIBM.SYSDUMMY1'; - if ($row) { - $info['version'] = $row[0].':'.$row[1]; - $info['fixpack'] = $row[1]; - $info['description'] = ''; - } else { - return ADOConnection::serverInfo(); - } + } else if (preg_match('/(\d{4})-(\d{2})-(\d{2})/',$col)) { + /* + * If the column is an ISO date string, we use it as is + */ + $trailer = 'FROM SYSIBM.SYSDUMMY1'; + if (substr($col,0,1) != "'") { + $col = $this->qstr($col); + } + } - return $info; - } + /* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */ + if ($fmt== 'Y-m-d H:i:s') + return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')"; - function createSequence($seqname='adodbseq',$start=1) - { - if (empty($this->_genSeqSQL)) - return false; + $s = ''; - $ok = $this->execute(sprintf($this->_genSeqSQL,$seqname,$start)); - if (!$ok) - return false; - return true; - } + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + if ($s) { + $s .= $this->concat_operator; + } - function dropSequence($seqname='adodbseq') - { - if (empty($this->_dropSeqSQL)) return false; - return $this->execute(sprintf($this->_dropSeqSQL,$seqname)); - } + $ch = $fmt[$i]; - function selectLimit($sql,$nrows=-1,$offset=-1,$inputArr=false,$secs2cache=0) - { - $nrows = (int) $nrows; + switch($ch) { + case 'Y': + case 'y': - if ($offset <= 0) - { - if ($nrows >= 0) - $sql .= " FETCH FIRST $nrows ROWS ONLY "; + if ($len==1) { + return "YEAR($col$dCol) $trailer"; + } - $rs = $this->execute($sql,$inputArr); + /* + * the CHAR creates a CHAR(20) so trim it + */ + $s .= "RTRIM(CHAR(YEAR($col$dCol)))"; - } - else - { - if ($offset > 0 && $nrows < 0); + break; - else - { - $nrows += $offset; - $sql .= " FETCH FIRST $nrows ROWS ONLY "; - } + case 'M': - /* - * DB2 has no native support for mid table offset - */ - $rs = ADOConnection::selectLimit($sql,$nrows,$offset,$inputArr); + /* + * Returns "January" we want "Jan" + */ + $cmd = "SUBSTR(MONTHNAME($col$dCol),1,3)"; - } + if ($len==1) { + return "$cmd $trailer"; + } + $s .= $cmd; - return $rs; - } + break; + case 'm': - function errorMsg() - { - if ($this->_errorMsg !== false) - return $this->_errorMsg; + $cmd = "RIGHT(DIGITS(MONTH($col$dCol)),2)"; + if ($len==1) { + return "$cmd $trailer"; + } - if (empty($this->_connectionID)) - return @db2_conn_errormsg(); + $s .= $cmd; + break; - return @db2_conn_errormsg($this->_connectionID); - } + case 'D': + case 'd': - function errorNo() - { + $cmd = "RIGHT(DIGITS(DAY($col$dCol)),2)"; - if ($this->_errorCode !== false) - return $this->_errorCode; + if ($len==1) { + return "$cmd $trailer"; + } + $s .= $cmd; + break; - if (empty($this->_connectionID)) - $e = @db2_conn_error(); + case 'H': + case 'h': - else - $e = @db2_conn_error($this->_connectionID); + $cmd = "RIGHT(DIGITS(HOUR($col$tCol)),2)"; + if ($len==1) { + return "$cmd $trailer"; + } - return $e; - } + $s .= $cmd; + break; + case 'i': + case 'I': - function beginTrans() - { - if (!$this->hasTransactions) - return false; - if ($this->transOff) - return true; + $cmd = "RIGHT(DIGITS(MINUTE($col$tCol)),2)"; + if ($len==1) { + return "$cmd $trailer"; + } - $this->transCnt += 1; + $s .= $cmd; - $this->_autocommit = false; + break; - return db2_autocommit($this->_connectionID,false); - } + case 'S': + case 's': + $cmd = "RIGHT(DIGITS(SECOND($col$tCol)),2)"; + if ($len==1) { + return "$cmd $trailer"; + } - function CommitTrans($ok=true) - { - if ($this->transOff) - return true; + $s .= $cmd; + break; - if (!$ok) - return $this->RollbackTrans(); + default: - if ($this->transCnt) - $this->transCnt -= 1; + if ($ch == '\\') { + $i++; + $ch = substr($fmt, $i, 1); + } - $this->_autocommit = true; - $ret = @db2_commit($this->_connectionID); - @db2_autocommit($this->_connectionID,true); - return $ret; - } + $s .= $this->qstr($ch); + } + } - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->_autocommit = true; - $ret = @db2_rollback($this->_connectionID); - @db2_autocommit($this->_connectionID,true); - return $ret; - } + $s .= " $trailer"; - /** - * 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) - { + return $s; + } - $primaryKeys = array(); + /** + * Return information about the database server + * + * @return array + */ + public function serverInfo() + { + global $ADODB_FETCH_MODE; + $savem = $ADODB_FETCH_MODE; - global $ADODB_FETCH_MODE; + $this->setFetchMode(ADODB_FETCH_NUM); - $schema = ''; - $this->_findschema($table,$schema); + $sql = "SELECT service_level, fixpack_num + FROM TABLE(sysproc.env_get_inst_info()) + AS INSTANCEINFO"; + $row = $this->GetRow($sql); - $table = $this->getTableCasedValue($table); + $this->setFetchMode($savem); - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $this->setFetchMode(ADODB_FETCH_NUM); + if ($row) { + $info['version'] = $row[0].':'.$row[1]; + $info['fixpack'] = $row[1]; + $info['description'] = ''; + } else { + return ADOConnection::serverInfo(); + } + return $info; + } - $sql = "SELECT * - FROM syscat.indexes - WHERE tabname='$table'"; + /** + * Select a limited number of rows. + * + * @param string $sql The SQL to execute + * @param int $nrows Number of rows to get + * @param int $offset Row to start calculations from (1-based) + * @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; - $rows = $this->getAll($sql); + if ($this->useLegacyRowOffsets) { + /* + * Disables support for OFFSET + */ + if ($offset <= 0) { + if ($nrows >= 0) + $sql .= " FETCH FIRST $nrows ROWS ONLY "; - $this->setFetchMode($savem); - $ADODB_FETCH_MODE = $savem; + $rs = $this->execute($sql, $inputArr); - if (empty($rows)) - return false; + } else { - foreach ($rows as $r) - { - if ($r[7] != 'P') - continue; + if ($offset > 0 && $nrows < 0) { - $cols = explode('+',$r[6]); - foreach ($cols as $colIndex=>$col) - { - if ($colIndex == 0) - continue; - $columnName = $this->getMetaCasedValue($col); - $primaryKeys[] = $columnName; - } - break; - } - return $primaryKeys; - } + } else { + $nrows += $offset; + $sql .= " FETCH FIRST $nrows ROWS ONLY "; + } - /** - * Returns a list of Foreign Keys associated with a specific table. - * - * @param string $table - * @param string $owner discarded - * @param bool $upper discarded - * @param bool $associative discarded - * - * @return string[]|false An array where keys are tables, and values are foreign keys; - * false if no foreign keys could be found. - */ - public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) - { + /* + * Disabled DB2 support for mid table offset + */ + $rs = ADOConnection::selectLimit($sql, $nrows, $offset, $inputArr); - global $ADODB_FETCH_MODE; + } + } else { - $schema = ''; - $this->_findschema($table,$schema); + $rowString = ''; + $offsetString = ''; - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($nrows > 0) { + $rowString = sprintf('FETCH FIRST %d ROWS ONLY', $nrows); + } - $this->setFetchMode(ADODB_FETCH_NUM); + if ($offset > 0) { + $offsetString = sprintf('OFFSET %d ROWS', $offset); + } - $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'"; + $sql = sprintf('%s %s %s', $sql, $offsetString, $rowString); + if ($secs2cache) { + $rs = $this->cacheExecute($secs2cache, $sql, $inputArr); + } else { + $rs = $this->execute($sql, $inputArr); + } + } - $results = $this->getAll($sql); + return $rs; + } - $ADODB_FETCH_MODE = $savem; - $this->setFetchMode($savem); - if (empty($results)) - return false; + function errorMsg() + { + if ($this->_errorMsg !== false) + return $this->_errorMsg; - $foreignKeys = array(); + if (empty($this->_connectionID)) + return @db2_conn_errormsg(); - foreach ($results as $r) - { - $parentTable = trim($this->getMetaCasedValue($r[2])); - $keyName = trim($this->getMetaCasedValue($r[1])); - $foreignKeys[$parentTable] = $keyName; - } + return @db2_conn_errormsg($this->_connectionID); + } - return $foreignKeys; - } + function errorNo() + { - /** - * 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) - { + if ($this->_errorCode !== false) + return $this->_errorCode; - global $ADODB_FETCH_MODE; - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if (empty($this->_connectionID)) + $e = @db2_conn_error(); - /* - * 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' - */ - $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'; - } + else + $e = @db2_conn_error($this->_connectionID); - if (!$schema) - $schema = '%'; + return $e; + } - if (!$mask) - $mask = '%'; - $qid = @db2_tables($this->_connectionID,NULL,$schema,$mask,$ttype); - $rs = new ADORecordSet_db2($qid); + function beginTrans() + { + if (!$this->hasTransactions) + return false; + if ($this->transOff) + return true; - $ADODB_FETCH_MODE = $savem; + $this->transCnt += 1; - if (!$rs) - return false; + $this->_autocommit = false; - $arr = $rs->getArray(); + return db2_autocommit($this->_connectionID,false); + } - $rs->Close(); + function CommitTrans($ok=true) + { + if ($this->transOff) + return true; - $tableList = array(); + if (!$ok) + return $this->RollbackTrans(); - /* - * 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. - */ + if ($this->transCnt) + $this->transCnt -= 1; - for ($i=0; $i < sizeof($arr); $i++) - { + $this->_autocommit = true; + $ret = @db2_commit($this->_connectionID); + @db2_autocommit($this->_connectionID,true); + return $ret; + } - $tableRow = $arr[$i]; - $tableName = $tableRow[2]; - $tableType = $tableRow[3]; + function RollbackTrans() + { + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $this->_autocommit = true; + $ret = @db2_rollback($this->_connectionID); + @db2_autocommit($this->_connectionID,true); + return $ret; + } - if (!$tableName) - continue; + /** + * 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) + { - if ($ttype == '' && (strcmp($tableType,'TABLE') <> 0 && strcmp($tableType,'VIEW') <> 0)) - continue; + $primaryKeys = array(); - /* - * Set metacasing if required - */ - $tableName = $this->getMetaCasedValue($tableName); + global $ADODB_FETCH_MODE; - /* - * If we requested a schema, we prepend the schema - name to the table name - */ - if (strcmp($schema,'%') <> 0) - $tableName = $schema . '.' . $tableName; + $schema = ''; + $this->_findschema($table,$schema); - $tableList[] = $tableName; + $table = $this->metaTables('T','',$table); + if ($table == false) + return false; - } - return $tableList; - } + $table = $table[0]; - /** - * 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) { + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $this->setFetchMode(ADODB_FETCH_NUM); - global $ADODB_FETCH_MODE; - /* Array( - * [name_of_index] => Array( - * [unique] => true or false - * [columns] => Array( - * [0] => firstcol - * [1] => nextcol - * [2] => etc........ - * ) - * ) - * ) - */ - $indices = array(); - $primaryKeyName = ''; + $sql = "SELECT * + FROM SYSCAT.INDEXES + WHERE TABNAME='$table'"; - $table = $this->getTableCasedValue($table); + $rows = $this->getAll($sql); + $this->setFetchMode($savem); + $ADODB_FETCH_MODE = $savem; - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $this->setFetchMode(ADODB_FETCH_NUM); + if (empty($rows)) + return false; - $sql = "SELECT * - FROM syscat.indexes - WHERE tabname='$table'"; + foreach ($rows as $r) + { + if ($r[7] != 'P') + continue; - $rows = $this->getAll($sql); + $cols = explode('+',$r[6]); + foreach ($cols as $colIndex=>$col) + { + if ($colIndex == 0) + continue; + $columnName = $this->getMetaCasedValue($col); + $primaryKeys[] = $columnName; + } + break; + } + return $primaryKeys; + } - $this->setFetchMode($savem); - $ADODB_FETCH_MODE = $savem; + /** + * Returns a list of Foreign Keys associated with a specific table. + * + * @param string $table + * @param string $owner discarded + * @param bool $upper discarded + * @param bool $associative discarded + * + * @return string[]|false An array where keys are tables, and values are foreign keys; + * false if no foreign keys could be found. + */ + public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) + { - if (empty($rows)) - return false; + global $ADODB_FETCH_MODE; - foreach ($rows as $r) - { + $schema = ''; + $this->_findschema($table,$schema); - $primaryIndex = $r[7] == 'P'?1:0; - if (!$primary) - /* - * Primary key not requested, ignore that one - */ - if ($r[7] == 'P') - continue; + $metaTables = $this->metaTables('T','',$table); - $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; - } + if ($metaTables == false) { + return false; + } - } + $table = $metaTables[0]; - return $indices; + $baseFetchMode = $ADODB_FETCH_MODE; - } + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; - /** - * 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) + $this->setFetchMode(ADODB_FETCH_ASSOC); + $sql = "SELECT * + FROM syscat.references + WHERE tabname = '$table'"; - * @return array of procedures on current database. - * - */ - public function metaProcedures($procedureNamePattern = null, $catalog = null, $schemaPattern = null) { + $results = $this->getAll($sql); + $this->setFetchMode($baseFetchMode); - global $ADODB_FETCH_MODE; + if (empty($results)) + return false; - $metaProcedures = array(); - $procedureSQL = ''; - $catalogSQL = ''; - $schemaSQL = ''; + $foreignKeys = array(); - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + foreach ($results as $r) + { - if ($procedureNamePattern) - $procedureSQL = "AND ROUTINENAME LIKE " . strtoupper($this->qstr($procedureNamePattern)); + /* - if ($catalog) - $catalogSQL = "AND OWNER=" . strtoupper($this->qstr($catalog)); + [CONSTNAME] => SQL250829011849680 + [TABSCHEMA] => DB2INST1 + [TABNAME] => TESTTABLE_2 + [OWNER] => DB2INST1 + [OWNERTYPE] => U + [REFKEYNAME] => SQL250829011847480 + [REFTABSCHEMA] => DB2INST1 + [REFTABNAME] => TESTTABLE_1 + [COLCOUNT] => 1 + [DELETERULE] => C + [UPDATERULE] => A + [CREATE_TIME] => 2025-08-29 01:18:49.717630 + [FK_COLNAMES] => TT_ID + [PK_COLNAMES] => ID + [DEFINER] => DB2INST1 + ) + */ + $referenceTable = trim($r['REFTABNAME']); - if ($schemaPattern) - $schemaSQL = "AND ROUTINESCHEMA LIKE {$this->qstr($schemaPattern)}"; + if (!array_key_exists($referenceTable,$foreignKeys)) { + $foreignKeys[$referenceTable] = array(); + } + $pkColnames = array_filter(preg_split('/ +/', $r['PK_COLNAMES'])); + $fkColnames = array_filter(preg_split('/ +/', $r['FK_COLNAMES'])); + foreach ($pkColnames as $i=>$pkColname) { - $fields = " - ROUTINENAME, - CASE ROUTINETYPE - WHEN 'P' THEN 'PROCEDURE' - WHEN 'F' THEN 'FUNCTION' - ELSE 'METHOD' - END AS ROUTINETYPE_NAME, - ROUTINESCHEMA, - REMARKS"; + $pkColname = trim($pkColname); + $fkColname = trim($fkColnames[$i]); + if ($baseFetchMode == ADODB_FETCH_ASSOC) { + $foreignKeys[$referenceTable][$pkColname] = $fkColname; + } else { + $foreignKeys[$referenceTable][] = sprintf('%s=%s',$pkColname,$fkColname); + } - $SQL = "SELECT $fields - FROM syscat.routines - WHERE OWNER IS NOT NULL - $procedureSQL - $catalogSQL - $schemaSQL - ORDER BY ROUTINENAME - "; + } - $result = $this->execute($SQL); + } + 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) + { - if (!$result) - return false; + global $ADODB_FETCH_MODE; - 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] - ); - } + $savem = $ADODB_FETCH_MODE; + $this->SetFetchMode(ADODB_FETCH_ASSOC); - return $metaProcedures; + /* + * 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' + */ - } + $sqlFilters = array(); + $sqlFilter = ''; + $returnedColumn = 'TRIM(TABNAME)'; - /** - * Lists databases. Because instances are independent, we only know about - * the current database name - * - * @return string[] - */ - public function metaDatabases(){ + if ($ttype) { - $dbName = $this->getMetaCasedValue($this->database); + /* + * We only support 'T' for TABLE and 'V' for VIEW + * All types are returned if $ttype is passed as '' + */ + $ttype = strtoupper(substr($ttype,0,1)); - return (array)$dbName; + if ($ttype == 'V') { + $sqlFilters[] = "TYPE='V'"; + } else if ($ttype == 'T') { + $sqlFilters[] = "TYPE='T'"; + } + } - } + if ($schema && $schema != '%') { + + $schema = strtoupper($schema); + $returnedColumn = $this->concat('TRIM(TABSCHEMA)',"'.'",'TRIM(TABNAME)'); + + if (strpos($schema,'%') !== false) { + $sqlFilters[] = "UPPER(TABSCHEMA) LIKE '$schema'"; + } else { + $sqlFilters[] = "UPPER(TABSCHEMA)='" . strtoupper($schema) . "'"; + } + } + + if ($mask) { + /* + * Mask is SQL LIKE format. We match on upper case + * unless the match is quoted in which case we match + * exactly + */ + + if (in_array(substr($mask,0,1),array('"','['))) + { + if (strpos($mask,'%') !== false) + { + $sqlFilters[] = "TABNAME LIKE '$mask'"; + } else { + $sqlFilters[] = "TABNAME='$mask'"; + } + } else { + $mask = strtoupper($mask); + if (strpos($mask,'%') !== false) { + $sqlFilters[] = "UPPER(TABNAME) LIKE '$mask'"; + } else { + $sqlFilters[] = "UPPER(TABNAME)='$mask'"; + } + } + } + + if (count($sqlFilters) > 0) { + $sqlFilter = 'WHERE ' . implode(' AND ',$sqlFilters); + } + + $sql = "SELECT $returnedColumn FROM SYSCAT.TABLES $sqlFilter ORDER BY TABSCHEMA, TABNAME"; + + + $metaTables = $this->getCol($sql); + + $this->SetFetchMode($savem); + + if (count($metaTables) == 0) + return false; + + return $metaTables; + } + + /** + * 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; + + /* 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; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $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 (empty($rows)) + return false; + + 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; + } + + } + + 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 $metaProcedures; + + } + + /** + * Lists databases. Because instances are independent, we only know about + * the current database name + * + * @return string[] + */ + public function metaDatabases(){ + + $dbName = $this->getMetaCasedValue($this->database); + + return (array)$dbName; + + } @@ -1057,965 +1232,998 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2 #define SQL_UNICODE_VARCHAR (-96) #define SQL_UNICODE_LONGVARCHAR (-97) */ - function DB2Types($t) - { - switch ((int)$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'; + function DB2Types($t) + { + switch ((int)$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 10: + case 11: + case 92: + case 93: + return 'T'; - case 9: - case 91: - return 'D'; + case 4: + case 5: + case -6: + return 'I'; - case 10: - case 11: - case 92: - case 93: - return 'T'; + case -11: // uniqidentifier + return 'R'; + case -7: //bit + return 'L'; - case 4: - case 5: - case -6: - return 'I'; + default: + return 'N'; + } + } - case -11: // uniqidentifier - return 'R'; - case -7: //bit - return 'L'; + public function metaColumns($table, $ignored=true) + { + global $ADODB_FETCH_MODE; - default: - return 'N'; - } - } + $schema = '%'; + $this->_findschema($table,$schema); - public function metaColumns($table, $normalize=true) - { - global $ADODB_FETCH_MODE; + $findMatchingTable = $this->metaTables('T','',$table); - $savem = $ADODB_FETCH_MODE; + if ($findMatchingTable === false) + 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; - } + $table = $findMatchingTable[0]; - $rs = new ADORecordSet_db2($qid); + $savem = $ADODB_FETCH_MODE; + $this->setFetchMode(ADODB_FETCH_NUM); + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - if (!$rs) - return false; + /* + * The returned table name has the correct casing + */ - $rs->_fetch(); + //$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; + } - $retarr = array(); + $rs = new ADORecordSet_db2($qid); - /* - $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 - 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 ($rs->fields[2] == $table) - { + if (!$rs) + return false; - $fld = new ADOFieldObject(); - $fld->name = $rs->fields[3]; - $fld->type = $this->DB2Types($rs->fields[4]); + $rs->_fetch(); - // 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 + $retarr = array(); - if ($fld->type == 'C' or $fld->type == 'X') { - 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]; + /* + $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 + 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) + { - $fld->not_null = !empty($rs->fields[10]); - $fld->scale = $rs->fields[8]; - $fld->primary_key = false; + if ($rs->fields[2] == $table) + { - //$columnName = $this->getMetaCasedValue($fld->name); - $columnName = strtoupper($fld->name); - $retarr[$columnName] = $fld; + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[3]; + $fld->type = $this->DB2Types($rs->fields[4]); - } - else if (sizeof($retarr)>0) - break; + // 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 - $rs->MoveNext(); + if ($fld->type == 'C' or $fld->type == 'X') { + 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]; + $fld->primary_key = 0; - $rs->Close(); - if (empty($retarr)) - $retarr = false; + //$columnName = $this->getMetaCasedValue($fld->name); + $columnName = strtoupper($fld->name); + $retarr[$columnName] = $fld; - /* - * Now we find out if the column is part of a primary key - */ + } + else if (sizeof($retarr)>0) + break; - $qid = @db2_primary_keys($this->_connectionID, "", $schema, $table); - if (empty($qid)) - return false; + $rs->MoveNext(); - $rs = new ADORecordSet_db2($qid); + } - if (!$rs) - { - $ADODB_FETCH_MODE = $savem; - return $retarr; - } - $rs->_fetch(); + $this->setFetchMode($savem); - /* - $rs->fields indices - 0 TABLE_CAT - 1 TABLE_SCHEM - 2 TABLE_NAME - 3 COLUMN_NAME - 4 KEY_SEQ - 5 PK_NAME - */ - while (!$rs->EOF) { - 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) - break; + $rs->Close(); + if (empty($retarr)) + $retarr = false; - $rs->MoveNext(); - } - $rs->Close(); - $ADODB_FETCH_MODE = $savem; + $this->setFetchMode(ADODB_FETCH_NUM ); - if (empty($retarr)) - return false; + /* + * Now we find out if the column is part of a primary key + */ + $primaryKeys = $this->metaPrimaryKeys($table); + if (!$primaryKeys) { + return $retarr; + } - /* - * If the fetch mode is numeric, return as numeric array - */ - if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) - $retarr = array_values($retarr); - return $retarr; - } + foreach ($primaryKeys as $pk) + { + $pk = strtoupper($pk); + if (isset($retarr[$pk])) { + $retarr[$pk]->primary_key = true; + } + } - /** - * 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) { + return $retarr; - 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); + $qid = @db2_primary_keys($this->_connectionID, "", $schema, $table); + if (empty($qid)) + return false; - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $rs = new ADORecordSet_db2($qid); - $qid = db2_procedures($this->_connectionID, NULL , '%' , $procedureName ); + if (!$rs) + { + $ADODB_FETCH_MODE = $savem; + return $retarr; + } + $rs->_fetch(); - $ADODB_FETCH_MODE = $savem; + /* + $rs->fields indices + 0 TABLE_CAT + 1 TABLE_SCHEM + 2 TABLE_NAME + 3 COLUMN_NAME + 4 KEY_SEQ + 5 PK_NAME + */ + while (!$rs->EOF) { - if (!$qid) - { - if ($this->debug) - ADOConnection::outp(sprintf('No Procedure of name %s available',$procedureName)); - return false; - } + 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) + break; + $rs->MoveNext(); + } + $rs->Close(); + $ADODB_FETCH_MODE = $savem; - $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; + if (empty($retarr)) + return false; - $qid = db2_procedure_columns($this->_connectionID, NULL , '%' , $procedureName , NULL ); + /* + * If the fetch mode is numeric, return as numeric array + */ + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) + $retarr = array_values($retarr); - $ADODB_FETCH_MODE = $savem; + return $retarr; + } - 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; + /** + * 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) { - $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(); + global $ADODB_FETCH_MODE; - } - $parameterCount = count($this->storedProcedureParameters['index']); - $parameterMarkers = array_fill(0,$parameterCount,'?'); + $this->storedProcedureParameters = array('name'=>'', + 'resource'=>false, + 'in'=>array(), + 'out'=>array(), + 'index'=>array(), + 'parameters'=>array(), + 'keyvalue' => array()); - /* - * We now know how many parameters to bind to the stored procedure - */ - $parameterList = implode(',',$parameterMarkers); + //$procedureName = strtoupper($procedureName); + //$procedureName = $this->getTableCasedValue($procedureName); - $sql = sprintf($preparedStatement,$procedureName,$parameterList); + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $spResource = @db2_prepare($this->_connectionID,$sql); + $qid = db2_procedures($this->_connectionID, NULL , '%' , $procedureName ); - if (!$spResource) - { - $errorMessage = @db2_conn_errormsg($this->_connectionID); - $this->_errorMsg = $errorMessage; + $ADODB_FETCH_MODE = $savem; - if ($this->debug) - ADOConnection::outp($errorMessage); + if (!$qid) + { + if ($this->debug) + ADOConnection::outp(sprintf('No Procedure of name %s available',$procedureName)); + return false; + } - return false; - } - $this->storedProcedureParameters['resource'] = $spResource; - if ($this->debug) - { + $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; - 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); + $qid = db2_procedure_columns($this->_connectionID, NULL , '%' , $procedureName , NULL ); - } + $ADODB_FETCH_MODE = $savem; - private function storedProcedureParameter(&$stmt, - &$var, - $name, - $isOutput=false, - $maxLen=4000, - $type=false) - { + 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(); - $name = strtoupper($name); + } + $parameterCount = count($this->storedProcedureParameters['index']); + $parameterMarkers = array_fill(0,$parameterCount,'?'); - /* - * 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); + /* + * We now know how many parameters to bind to the stored procedure + */ + $parameterList = implode(',',$parameterMarkers); - $this->_errorMsg = $errorMessage; - if ($this->debug) - ADOConnection::outp($errorMessage); - return false; - } + $sql = sprintf($preparedStatement,$procedureName,$parameterList); - if (!$isOutput - && !isset( $this->storedProcedureParameters['in'][$name])) - { - $errorMessage = sprintf('%s is not a valid IN parameter name',$name); + $spResource = @db2_prepare($this->_connectionID,$sql); - $this->_errorMsg = $errorMessage; - if ($this->debug) - ADOConnection::outp($errorMessage); - return false; - } + if (!$spResource) + { + $errorMessage = @db2_conn_errormsg($this->_connectionID); + $this->_errorMsg = $errorMessage; - /* - * We will use these values to bind to when we execute - * the query - */ - $this->storedProcedureParameters['keyvalue'][$name] = &$var; + if ($this->debug) + ADOConnection::outp($errorMessage); - return true; + return false; + } - } + $this->storedProcedureParameters['resource'] = $spResource; - /** - * 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() - { + if ($this->debug) + { - /* - * Get the previously built resource - */ - $stmtid = $this->storedProcedureParameters['resource']; + 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); - /* - * 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]; + private function storedProcedureParameter(&$stmt, + &$var, + $name, + $isOutput=false, + $maxLen=4000, + $type=false) + { - /* - * 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 - ); + $name = strtoupper($name); - if (!$ok) - { - $this->_errorMsg = @db2_stmt_errormsg(); - $this->_errorCode = @db2_stmt_error(); + /* + * 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); - if ($this->debug) - ADOConnection::outp($this->_errorMsg); - return false; - } + $this->_errorMsg = $errorMessage; + if ($this->debug) + ADOConnection::outp($errorMessage); + return false; + } - if ($this->debug) - ADOConnection::outp("Correctly Bound parameter $spName to procedure"); + if (!$isOutput + && !isset( $this->storedProcedureParameters['in'][$name])) + { + $errorMessage = sprintf('%s is not a valid IN parameter name',$name); - /* - * Build a variable in the current environment that matches - * the parameter name - */ - ${$spName} = $spValue; + $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; - /* - * All bound, execute - */ + return true; - if (!@db2_execute($stmtid)) - { - $this->_errorMsg = @db2_stmt_errormsg(); - $this->_errorCode = @db2_stmt_error(); + } - if ($this->debug) - ADOConnection::outp($this->_errorMsg); - return false; - } + /** + * 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() + { - /* - * 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; - } + /* + * Get the previously built resource + */ + $stmtid = $this->storedProcedureParameters['resource']; - return $stmtid; - } + /* + * Bind our variables to the DB2 procedure + */ + foreach ($this->storedProcedureParameters['keyvalue'] as $spName=>$spValue){ - /** - * - * 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) - { + /* + * Get the ordinal position, required for binding + */ + $ordinalPosition = $this->storedProcedureParameters['index'][$spName]; - /* - * 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); + /* + * 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 + ); - /* - * We are going to add a parameter to a prepared statement - */ - if ($this->debug) - ADOConnection::outp("Adding parameter to prepared statement"); - } + if (!$ok) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + if ($this->debug) + ADOConnection::outp($this->_errorMsg); + return false; + } - /** - * Prepares a prepared SQL statement, not used for stored procedures - * - * @param string $sql - * - * @return mixed - */ - function prepare($sql) - { + if ($this->debug) + ADOConnection::outp("Correctly Bound parameter $spName to procedure"); - if (! $this->_bindInputArray) return $sql; // no binding + /* + * Build a variable in the current environment that matches + * the parameter name + */ + ${$spName} = $spValue; - $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; - } - return array($sql,$stmt,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) - { - $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; + /* + * All bound, execute + */ - case ADODB_ASSOC_CASE_LOWER: - $db2Options = array('db2_attr_case'=>DB2_CASE_LOWER); - $setOption = @db2_set_option($this->_connectionID,$db2Options,1); - break; + if (!@db2_execute($stmtid)) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); - default: - $db2Options = array('db2_attr_case'=>DB2_CASE_NATURAL); - $setOption = @db2_set_option($this->_connectionID,$db2Options,1); - } + if ($this->debug) + ADOConnection::outp($this->_errorMsg); + return false; + } - if ($inputarr) - { - if (is_array($sql)) - { - $stmtid = $sql[1]; - } - else - { - $stmtid = @db2_prepare($this->_connectionID,$sql); + /* + * 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; + } - if ($stmtid == false) - { - $this->_errorMsg = @db2_stmt_errormsg(); - $this->_errorCode = @db2_stmt_error(); + return $stmtid; + } - if ($this->debug) - ADOConnection::outp($this->_errorMsg); + /** + * + * 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) + { - return 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); - 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)) - { + /* + * We are going to add a parameter to a prepared statement + */ + if ($this->debug) + ADOConnection::outp("Adding parameter to prepared statement"); + } - /* - * 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(); + /** + * Prepares a prepared SQL statement, not used for stored procedures + * + * @param string $sql + * + * @return mixed + */ + function prepare($sql) + { - /* - * 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); + if (! $this->_bindInputArray) return $sql; // no binding - 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; - } - } + $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; + } + return array($sql,$stmt,false); + } - if (!@db2_execute($stmtid)) - { - $this->_errorMsg = @db2_stmt_errormsg(); - $this->_errorCode = @db2_stmt_error(); - if ($this->debug) - ADOConnection::outp($this->_errorMsg); - return 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) + { + $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; - return $stmtid; - } - else - { + case ADODB_ASSOC_CASE_LOWER: + $db2Options = array('db2_attr_case'=>DB2_CASE_LOWER); + $setOption = @db2_set_option($this->_connectionID,$db2Options,1); + break; - $stmtid = @db2_exec($this->_connectionID,$sql); - } - $this->_lastAffectedRows = 0; - if ($stmtid) - { - if (@db2_num_fields($stmtid) == 0) - { - $this->_lastAffectedRows = db2_num_rows($stmtid); - $stmtid = true; - } - else - { - $this->_lastAffectedRows = 0; - } + default: + $db2Options = array('db2_attr_case'=>DB2_CASE_NATURAL); + $setOption = @db2_set_option($this->_connectionID,$db2Options,1); + } - $this->_errorMsg = ''; - $this->_errorCode = 0; + if (!$this->useLegacyRecordCounting) { + $db2Options = array('rowcount'=>DB2_ROWCOUNT_PREFETCH_ON); + $setOption = @db2_set_option($this->_connectionID,$db2Options,1); + } - } - else - { + if ($inputarr) + { + if (is_array($sql)) + { + $stmtid = $sql[1]; + } + else + { + $stmtid = @db2_prepare($this->_connectionID,$sql); - $this->_errorMsg = @db2_stmt_errormsg(); - $this->_errorCode = @db2_stmt_error(); + if ($stmtid == false) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); - } - return $stmtid; - } + if ($this->debug) + ADOConnection::outp($this->_errorMsg); - /* - Insert a null into the blob field of the table first. - Then use UpdateBlob to store the blob. + return false; + } + } - Usage: + if (! @db2_execute($stmtid,$inputarr)) + { + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + if ($this->debug) + ADOConnection::outp($this->_errorMsg); + return false; + } - $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') - { - return $this->execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; - } + } + else if (is_array($sql)) + { - // returns true or false - function _close() - { - $ret = @db2_close($this->_connectionID); - $this->_connectionID = false; - return $ret; - } + /* + * Either a prepared statement or a stored procedure + */ - function _affectedrows() - { - return $this->_lastAffectedRows; - } + if (is_array($this->storedProcedureParameters) + && is_resource($this->storedProcedureParameters['resource'] + )) + /* + * This is all handled in the separate method for + * readability + */ + return $this->executeStoredProcedure(); - /** - * 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; + /* + * 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); - switch($ADODB_ASSOC_CASE) - { - case ADODB_ASSOC_CASE_LOWER: - $value = strtolower($value); - break; - case ADODB_ASSOC_CASE_UPPER: - $value = strtoupper($value); - break; - } - return $value; - } + 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; + } - const TABLECASE_LOWER = 0; - const TABLECASE_UPPER = 1; - const TABLECASE_DEFAULT = 2; + return $stmtid; + } + else + { - /** - * Controls the casing of the table provided to the meta functions - */ - private $tableCase = 2; + $stmtid = @db2_exec($this->_connectionID,$sql); + } + $this->_lastAffectedRows = 0; + if ($stmtid) + { + if (@db2_num_fields($stmtid) == 0) + { + $this->_lastAffectedRows = db2_num_rows($stmtid); + $stmtid = true; + } + else + { + $this->_lastAffectedRows = 0; + } - /** - * Sets the table case parameter - * - * @param int $caseOption - * @return null - */ - final public function setTableCasing($caseOption) - { - $this->tableCase = $caseOption; - } + $this->_errorMsg = ''; + $this->_errorCode = 0; - /** - * Gets the table casing parameter - * - * @return int $caseOption - */ - final public function getTableCasing() - { - return $this->tableCase; - } + } + else + { - /** - * 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; - } + $this->_errorMsg = @db2_stmt_errormsg(); + $this->_errorCode = @db2_stmt_error(); + + } + return $stmtid; + } + + /* + 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') + { + return $this->execute("UPDATE $table SET $column=? WHERE $where",$val) != false; + } + + // returns true or false + function _close() + { + $ret = @db2_close($this->_connectionID); + $this->_connectionID = false; + return $ret; + } + + function _affectedrows() + { + 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; + } } /*-------------------------------------------------------------------------------------- - Class Name: Recordset + Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_db2 extends ADORecordSet { - var $bind = false; - var $databaseType = "db2"; - var $dataProvider = "db2"; - var $useFetchArray; - - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $id; - } - + var $bind = false; + var $databaseType = "db2"; + var $dataProvider = "db2"; + var $useFetchArray; - // returns the field object - 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); - */ - return $o; - } + // returns the field object + 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); - /* Use associative array to get fields array */ - function fields($colname) - { + /* + if (ADODB_ASSOC_CASE == 0) + $o->name = strtolower($o->name); + else if (ADODB_ASSOC_CASE == 1) + $o->name = strtoupper($o->name); + */ + return $o; + } - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - return $this->fields[$colname]; - } + /* Use associative array to get fields array */ + function 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; - } - } + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + return $this->fields[$colname]; + } - return $this->fields[$this->bind[strtoupper($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 _initrs() - { - global $ADODB_COUNTRECS; - $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1; - $this->_numOfFields = @db2_num_fields($this->_queryID); + function _initrs() + { + global $ADODB_COUNTRECS; + /* + * If legacy rowcount is activated, this will return -1; + */ + $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1; - // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 + $this->_numOfFields = @db2_num_fields($this->_queryID); - if ($this->_numOfRows == 0) - $this->_numOfRows = -1; - } + if ($this->_numOfRows == 0) + $this->_numOfRows = -1; + } - function _seek($row) - { - return false; - } + function _seek($row) + { + return false; + } - function getArrayLimit($nrows,$offset=0) - { - if ($offset <= 0) { - $rs = $this->GetArray($nrows); - return $rs; - } + function getArrayLimit($nrows,$offset=0) + { + if ($offset <= 0) { + $rs = $this->GetArray($nrows); + return $rs; + } - $this->Move($offset); + $this->Move($offset); - $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->EOF || $this->_numOfRows == 0) - return false; + function moveNext() + { + if ($this->EOF || $this->_numOfRows == 0) + return false; - $this->_currentRow++; + $this->_currentRow++; - $this->processCoreFetch(); - return $this->processMoveRecord(); + $this->processCoreFetch(); + return $this->processMoveRecord(); - } + } - private function processCoreFetch() - { - switch ($this->fetchMode){ - case ADODB_FETCH_ASSOC: + private function processCoreFetch() + { + switch ($this->fetchMode){ + case ADODB_FETCH_ASSOC: - /* - * Associative array - */ - $this->fields = @db2_fetch_assoc($this->_queryID); - break; + /* + * 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); - 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); + break; + } + } - private function processMoveRecord() - { - if (!$this->fields){ - $this->EOF = true; - return false; - } + private function processMoveRecord() + { + if (!$this->fields){ + $this->EOF = true; + return false; + } - return true; - } + return true; + } - function _fetch() - { - $this->processCoreFetch(); - if ($this->fields) - return true; + function _fetch() + { + $this->processCoreFetch(); + if ($this->fields) + return true; - $this->fields = false; - return false; - } + $this->fields = false; + return false; + } - function _close() - { - $ok = @db2_free_result($this->_queryID); - if (!$ok) - { - $this->connection->_errorMsg = @db2_stmt_errormsg($this->_queryID); - $this->connection->_errorCode = @db2_stmt_error(); + function _close() + { + $ok = @db2_free_result($this->_queryID); + if (!$ok) + { + $this->connection->_errorMsg = @db2_stmt_errormsg($this->_queryID); + $this->connection->_errorCode = @db2_stmt_error(); - if ($this->debug) - ADOConnection::outp($this->connection->_errorMsg); - return false; - } - } + if ($this->debug) + ADOConnection::outp($this->connection->_errorMsg); + return false; + } + } } diff --git a/drivers/adodb-fbsql.inc.php b/drivers/adodb-fbsql.inc.php index 64913bc2..cfbadf99 100644 --- a/drivers/adodb-fbsql.inc.php +++ b/drivers/adodb-fbsql.inc.php @@ -169,20 +169,22 @@ class ADORecordSet_fbsql extends ADORecordSet{ var $databaseType = "fbsql"; var $canSeek = true; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - if (!$mode) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) { - case ADODB_FETCH_NUM: $this->fetchMode = FBSQL_NUM; break; - case ADODB_FETCH_ASSOC: $this->fetchMode = FBSQL_ASSOC; break; - case ADODB_FETCH_BOTH: - default: - $this->fetchMode = FBSQL_BOTH; break; + parent::__construct($queryID, $mode); + + switch ($this->adodbFetchMode) { + case ADODB_FETCH_NUM: + $this->fetchMode = FBSQL_NUM; + break; + case ADODB_FETCH_ASSOC: + $this->fetchMode = FBSQL_ASSOC; + break; + case ADODB_FETCH_BOTH: + default: + $this->fetchMode = FBSQL_BOTH; + break; } - parent::__construct($queryID); } function _initrs() diff --git a/drivers/adodb-firebird.inc.php b/drivers/adodb-firebird.inc.php index 52d751d8..6d495973 100644 --- a/drivers/adodb-firebird.inc.php +++ b/drivers/adodb-firebird.inc.php @@ -1020,28 +1020,10 @@ class ADORecordset_firebird extends ADORecordSet private $fieldObjects = false; /** - * @var bool Flags if we have retrieved the metadata - */ - private $fieldObjectsRetrieved = false; - - /** - * @var array Cross-reference the objects by name for easy access - */ - private $fieldObjectsIndex = array(); - - /** * @var bool Flag to indicate if the result has a blob */ private $fieldObjectsHaveBlob = false; - function __construct($id, $mode = false) - { - global $ADODB_FETCH_MODE; - - $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; - parent::__construct($id); - } - /** * Returns: an object containing field information. diff --git a/drivers/adodb-ibase.inc.php b/drivers/adodb-ibase.inc.php index 7a37976b..1eaf07fe 100644 --- a/drivers/adodb-ibase.inc.php +++ b/drivers/adodb-ibase.inc.php @@ -741,13 +741,6 @@ class ADORecordSet_ibase extends ADORecordSet var $bind=false; var $_cacheType; - function __construct($id,$mode=false) - { - global $ADODB_FETCH_MODE; - - $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; - parent::__construct($id); - } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about diff --git a/drivers/adodb-informix72.inc.php b/drivers/adodb-informix72.inc.php index 6fddde3d..d4083d5e 100644 --- a/drivers/adodb-informix72.inc.php +++ b/drivers/adodb-informix72.inc.php @@ -399,17 +399,6 @@ class ADORecordset_informix72 extends ADORecordSet { var $canSeek = true; var $_fieldprops = false; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($id); - } - - /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about diff --git a/drivers/adodb-ldap.inc.php b/drivers/adodb-ldap.inc.php index 55d4334a..032abbe5 100644 --- a/drivers/adodb-ldap.inc.php +++ b/drivers/adodb-ldap.inc.php @@ -31,70 +31,101 @@ if (!defined('LDAP_ASSOC')) { define('LDAP_BOTH',ADODB_FETCH_BOTH); } -class ADODB_ldap extends ADOConnection { - var $databaseType = 'ldap'; - var $dataProvider = 'ldap'; +class ADODB_ldap extends ADOConnection +{ + public $databaseType = 'ldap'; + public $dataProvider = 'ldap'; - # Connection information - var $username = false; - var $password = false; + /** + * @var string[] Caches the version info + */ + protected $version; - # Used during searches - var $filter; - var $dn; - var $version; - var $port = 389; + /** + * @var string Bind error message, eg. "Binding: invalid credentials" + */ + protected $_bind_errmsg = "Binding: %s"; - # Options configuration information - var $LDAP_CONNECT_OPTIONS; - - # error on binding, eg. "Binding: invalid credentials" - var $_bind_errmsg = "Binding: %s"; + protected $connectionParameters = array( + LDAP_OPT_PROTOCOL_VERSION => 3, + LDAP_OPT_REFERRALS => 0 + ); - // returns true or false - function _connect( $host, $username, $password, $ldapbase) + /** + * Connect to an LDAP Server + * + * @param string|null $ldapServer The LDAP Server to connect to. + * @param string|null $username The username to connect as. + * @param string|null $password The password to connect with. + * @param string|null $ldapbase The Base DN + * + * @return bool|null True if connected successfully, false if connection failed, or null if the ldap extension + * isn't currently loaded. + */ + public function _connect($ldapServer, $username, $password, $ldapbase) { global $LDAP_CONNECT_OPTIONS; - if ( !function_exists( 'ldap_connect' ) ) return null; + if (!function_exists('ldap_connect')) { + return null; + } - if (strpos($host,'ldap://') === 0 || strpos($host,'ldaps://') === 0) { - $this->_connectionID = @ldap_connect($host); - } else { - $conn_info = array( $host,$this->port); + if (strpos($ldapServer, 'ldap://') !== 0 && strpos($ldapServer, 'ldaps://') !== 0) { + /* + * If ldap SSL not explicitly specified, fall back to insecure + */ + $ldapServer = sprintf('ldap://%s', $ldapServer); + } - if ( strstr( $host, ':' ) ) { - $conn_info = explode( ':', $host ); - } + $this->_connectionID = @ldap_connect($ldapServer); - $this->_connectionID = @ldap_connect( $conn_info[0], $conn_info[1] ); - } if (!$this->_connectionID) { - $e = 'Could not connect to ' . $conn_info[0]; + $e = 'Could not connect to ' . $ldapServer; $this->_errorMsg = $e; - if ($this->debug) ADOConnection::outp($e); + if ($this->debug) { + ADOConnection::outp($e); + } + return false; } - if(!empty($LDAP_CONNECT_OPTIONS)) { - $this->_inject_bind_options( $LDAP_CONNECT_OPTIONS ); + + if(!empty($LDAP_CONNECT_OPTIONS) && is_array($LDAP_CONNECT_OPTIONS)) { + // Convert options to connectionParameters() + trigger_error('$LDAP_CONNECT_OPTIONS is deprecated, use setConnectionParameter() instead', E_USER_DEPRECATED); + $this->_inject_bind_options($LDAP_CONNECT_OPTIONS); + } + + // Iterate over any connection parameters + foreach ($this->connectionParameters as $parameter => $value) { + if (!ldap_set_option($this->_connectionID, $parameter, $value)) { + if ($this->debug) { + $message = sprintf('Warning - Setting connection parameter %s to value %s failed', $parameter, + $value); + ADOConnection::outp($message); + $this->_errorMsg = $message; + } + } } if ($username) { - $bind = @ldap_bind( $this->_connectionID, $username, $password ); + $bind = @ldap_bind($this->_connectionID, $username, $password); } else { - $username = 'anonymous'; - $bind = @ldap_bind( $this->_connectionID ); + $bind = @ldap_bind($this->_connectionID); } if (!$bind) { - $e = sprintf($this->_bind_errmsg,ldap_error($this->_connectionID)); + $e = sprintf($this->_bind_errmsg, ldap_error($this->_connectionID)); $this->_errorMsg = $e; - if ($this->debug) ADOConnection::outp($e); + if ($this->debug) { + ADOConnection::outp($e); + } return false; } + $this->_errorMsg = ''; $this->database = $ldapbase; + return $this->_connectionID; } @@ -150,48 +181,95 @@ class ADODB_ldap extends ADOConnection { ); */ - function _inject_bind_options( $options ) { - foreach( $options as $option ) { - ldap_set_option( $this->_connectionID, $option["OPTION_NAME"], $option["OPTION_VALUE"] ) - or die( "Unable to set server option: " . $option["OPTION_NAME"] ); + /** + * Converts old style global options into connection parameters + * + * @param array $options + * @return void + * @deprecated 5.23.0 + */ + private function _inject_bind_options($options) + { + foreach ($options as $option) { + $this->connectionParameters[$option["OPTION_NAME"]] = $option["OPTION_VALUE"]; + //ldap_set_option( $this->_connectionID, $option["OPTION_NAME"], $option["OPTION_VALUE"] ); + // or die( "Unable to set server option: " . $option["OPTION_NAME"] ); } } - function _query($sql,$inputarr=false) + + /** + * Execute a query against the AD Database. + * + * @param string|array $ldapQuery Query to execute. + * @param array $inputarr [Ignored] An optional array of parameters. + * + * @return resource|false + * @noinspection PhpParameterNameChangedDuringInheritanceInspection + */ + function _query($ldapQuery, $inputarr = false) { - $rs = @ldap_search( $this->_connectionID, $this->database, $sql ); - $this->_errorMsg = ($rs) ? '' : 'Search error on '.$sql.': '.ldap_error($this->_connectionID); + $rs = ldap_search($this->_connectionID, $this->database, $ldapQuery); + + $this->_errorMsg = ($rs) ? '' : 'Search error on ' . $ldapQuery . ': ' . ldap_error($this->_connectionID); + return $rs; } - function ErrorMsg() - { - return $this->_errorMsg; - } - function ErrorNo() + /** + * Returns the last error number from previous AD operation. + * + * @return int The last error number. + */ + function errorNo() { return @ldap_errno($this->_connectionID); } - /* closes the LDAP connection */ + /** + * closes the LDAP connection + * + * @return void + */ function _close() { - @ldap_close( $this->_connectionID ); + if (is_resource($this->_connectionID)) { + @ldap_close($this->_connectionID); + } + $this->_connectionID = false; } - function SelectDB($db) { - $this->database = $db; + /** + * Switches the baseDN to a new value. Just like _connect(), + * its impossible to determine if the parameter is correct + * until you issue a query + * + * @return bool true + * @noinspection PhpParameterNameChangedDuringInheritanceInspection + */ + public function selectDB($baseDN) + { + $this->database = $baseDN; return true; - } // SelectDB + } - function ServerInfo() + /** + * Get server version info. + * + * @return string[]|false Array with multiple string elements that define the connection + */ + public function serverInfo() { - if( !empty( $this->version ) ) { + if (!empty($this->version)) { return $this->version; } + if (!is_resource($this->_connectionID)) { + return false; + } + $version = array(); /* Determines how aliases are handled during search. @@ -204,16 +282,20 @@ class ADODB_ldap extends ADOConnection { aliases are dereferenced when locating the base object but not during the search. Default: LDAP_DEREF_NEVER */ - ldap_get_option( $this->_connectionID, LDAP_OPT_DEREF, $version['LDAP_OPT_DEREF'] ) ; - switch ( $version['LDAP_OPT_DEREF'] ) { + ldap_get_option($this->_connectionID, LDAP_OPT_DEREF, $version['LDAP_OPT_DEREF']); + switch ($version['LDAP_OPT_DEREF']) { case 0: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_NEVER'; + break; case 1: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_SEARCHING'; + break; case 2: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_FINDING'; + break; case 3: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_ALWAYS'; + break; } /* @@ -221,8 +303,8 @@ class ADODB_ldap extends ADOConnection { LDAP_NO_LIMIT (0) means no limit. Default: LDAP_NO_LIMIT */ - ldap_get_option( $this->_connectionID, LDAP_OPT_SIZELIMIT, $version['LDAP_OPT_SIZELIMIT'] ); - if ( $version['LDAP_OPT_SIZELIMIT'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_SIZELIMIT, $version['LDAP_OPT_SIZELIMIT']); + if ($version['LDAP_OPT_SIZELIMIT'] == 0) { $version['LDAP_OPT_SIZELIMIT'] = 'LDAP_NO_LIMIT'; } @@ -231,8 +313,8 @@ class ADODB_ldap extends ADOConnection { LDAP_NO_LIMIT (0) means no limit. Default: LDAP_NO_LIMIT */ - ldap_get_option( $this->_connectionID, LDAP_OPT_TIMELIMIT, $version['LDAP_OPT_TIMELIMIT'] ); - if ( $version['LDAP_OPT_TIMELIMIT'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_TIMELIMIT, $version['LDAP_OPT_TIMELIMIT']); + if ($version['LDAP_OPT_TIMELIMIT'] == 0) { $version['LDAP_OPT_TIMELIMIT'] = 'LDAP_NO_LIMIT'; } @@ -242,8 +324,8 @@ class ADODB_ldap extends ADOConnection { LDAP_OPT_OFF Default: ON */ - ldap_get_option( $this->_connectionID, LDAP_OPT_REFERRALS, $version['LDAP_OPT_REFERRALS'] ); - if ( $version['LDAP_OPT_REFERRALS'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_REFERRALS, $version['LDAP_OPT_REFERRALS']); + if ($version['LDAP_OPT_REFERRALS'] == 0) { $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_OFF'; } else { $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_ON'; @@ -255,8 +337,8 @@ class ADODB_ldap extends ADOConnection { LDAP_OPT_OFF Default: OFF */ - ldap_get_option( $this->_connectionID, LDAP_OPT_RESTART, $version['LDAP_OPT_RESTART'] ); - if ( $version['LDAP_OPT_RESTART'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_RESTART, $version['LDAP_OPT_RESTART']); + if ($version['LDAP_OPT_RESTART'] == 0) { $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_OFF'; } else { $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_ON'; @@ -268,18 +350,18 @@ class ADODB_ldap extends ADOConnection { LDAP_VERSION3 (3) Default: LDAP_VERSION2 (2) */ - ldap_get_option( $this->_connectionID, LDAP_OPT_PROTOCOL_VERSION, $version['LDAP_OPT_PROTOCOL_VERSION'] ); - if ( $version['LDAP_OPT_PROTOCOL_VERSION'] == 2 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_PROTOCOL_VERSION, $version['LDAP_OPT_PROTOCOL_VERSION']); + if ($version['LDAP_OPT_PROTOCOL_VERSION'] == 2) { $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION2'; } else { $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION3'; } /* The host name (or list of hosts) for the primary LDAP server. */ - ldap_get_option( $this->_connectionID, LDAP_OPT_HOST_NAME, $version['LDAP_OPT_HOST_NAME'] ); - ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_NUMBER, $version['LDAP_OPT_ERROR_NUMBER'] ); - ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_STRING, $version['LDAP_OPT_ERROR_STRING'] ); - ldap_get_option( $this->_connectionID, LDAP_OPT_MATCHED_DN, $version['LDAP_OPT_MATCHED_DN'] ); + ldap_get_option($this->_connectionID, LDAP_OPT_HOST_NAME, $version['LDAP_OPT_HOST_NAME']); + ldap_get_option($this->_connectionID, LDAP_OPT_ERROR_NUMBER, $version['LDAP_OPT_ERROR_NUMBER']); + ldap_get_option($this->_connectionID, LDAP_OPT_ERROR_STRING, $version['LDAP_OPT_ERROR_STRING']); + ldap_get_option($this->_connectionID, LDAP_OPT_MATCHED_DN, $version['LDAP_OPT_MATCHED_DN']); return $this->version = $version; } @@ -289,140 +371,206 @@ class ADODB_ldap extends ADOConnection { Class Name: Recordset --------------------------------------------------------------------------------------*/ -class ADORecordSet_ldap extends ADORecordSet{ +class ADORecordSet_ldap extends ADORecordSet +{ + + public $databaseType = "ldap"; + public $canSeek = false; + + protected $_entryID; /* keeps track of the entry resource identifier */ - var $databaseType = "ldap"; - var $canSeek = false; - var $_entryID; /* keeps track of the entry resource identifier */ + protected $seekData = array(); - function __construct($queryID,$mode=false) + /** + * Constructor + * + * @param resource $queryID + * @param boolean $mode + */ + public function __construct($queryID, $mode = false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) - { - case ADODB_FETCH_NUM: - $this->fetchMode = LDAP_NUM; - break; - case ADODB_FETCH_ASSOC: - $this->fetchMode = LDAP_ASSOC; - break; - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH: - default: - $this->fetchMode = LDAP_BOTH; - break; - } + parent::__construct($queryID, $mode); - parent::__construct($queryID); + switch ($this->adodbFetchMode) { + case ADODB_FETCH_NUM: + $this->fetchMode = LDAP_NUM; + break; + case ADODB_FETCH_ASSOC: + $this->fetchMode = LDAP_ASSOC; + break; + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH: + default: + $this->fetchMode = LDAP_BOTH; + break; + } } - function _initrs() + /** + * Recordset initialization stub + * + * @return void + */ + protected function _initrs() { - /* - This could be teaked to respect the $COUNTRECS directive from ADODB - It's currently being used in the _fetch() function and the - GetAssoc() function - */ - $this->_numOfRows = ldap_count_entries( $this->connection->_connectionID, $this->_queryID ); + // This could be tweaked to respect the $COUNTRECS directive from ADODB + // It's currently being used in the _fetch() function and the GetAssoc() function + $this->_numOfRows = ldap_count_entries($this->connection->_connectionID, $this->_queryID); } - /* - Return whole recordset as a multi-dimensional associative array - */ - function GetAssoc($force_array = false, $first2cols = false, $fetchMode = -1) + + /** + * 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 + */ + public function fetchField($fieldOffset = -1) { - $records = $this->_numOfRows; - $results = array(); - for ( $i=0; $i < $records; $i++ ) { - foreach ( $this->fields as $k=>$v ) { - if ( is_array( $v ) ) { - if ( $v['count'] == 1 ) { - $results[$i][$k] = $v[0]; - } else { - array_shift( $v ); - $results[$i][$k] = $v; - } - } - } + + if (!array_key_exists($fieldOffset, $this->seekData)) { + return false; } - return $results; + $o = new ADOFieldObject; + $o->name = $this->seekData[$fieldOffset]; + $o->max_length = 1024; + $o->type = 'C'; + + return $o; } - function GetRowAssoc($upper = ADODB_ASSOC_CASE) + /** + * 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. + */ + public function _fetch() { - $results = array(); - foreach ( $this->fields as $k=>$v ) { - if ( is_array( $v ) ) { - if ( $v['count'] == 1 ) { - $results[$k] = $v[0]; - } else { - array_shift( $v ); - $results[$k] = $v; - } - } + if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) { + $this->EOF = true; + return false; } + $this->EOF = false; - return $results; - } - - function GetRowNums() - { - $results = array(); - foreach ( $this->fields as $k=>$v ) { - static $i = 0; - if (is_array( $v )) { - if ( $v['count'] == 1 ) { - $results[$i] = $v[0]; - } else { - array_shift( $v ); - $results[$i] = $v; - } - $i++; - } + if ($this->_currentRow == 0) { + $this->_entryID = ldap_first_entry($this->connection->_connectionID, $this->_queryID); + } else { + $this->_entryID = ldap_next_entry($this->connection->_connectionID, $this->_entryID); } - return $results; - } - function _fetch() - { - if ( $this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0 ) { + $r = ldap_get_attributes($this->connection->_connectionID, $this->_entryID); + + if (!$r) { + $this->EOF = true; return false; } - if ( $this->_currentRow == 0 ) { - $this->_entryID = ldap_first_entry( $this->connection->_connectionID, $this->_queryID ); - } else { - $this->_entryID = ldap_next_entry( $this->connection->_connectionID, $this->_entryID ); + $rowentries = $r['count']; + unset($r['count']); + + $objectClass = false; + + if ($r[0] == 'objectClass') { + /* + * Always an array, move to the end of the row we want to use getAssoc + */ + $objectClass = $r['objectClass']; + unset($r[0]); + unset($r['objectClass']); + unset($objectClass['count']); } - $this->fields = ldap_get_attributes( $this->connection->_connectionID, $this->_entryID ); - $this->_numOfFields = $this->fields['count']; + $rowData = array(); + $rowIndex = 0; + for ($rowentry = 0; $rowentry < $rowentries; $rowentry++) { + if (!array_key_exists($rowentry, $r)) /* + * We moved objectClass + */ { + continue; + } - switch ( $this->fetchMode ) { + $eName = $r[$rowentry]; + $eData = $r[$eName]; + $dataEntries = $eData['count']; - case LDAP_ASSOC: - $this->fields = $this->GetRowAssoc(); - break; + unset($eData['count']); - case LDAP_NUM: - $this->fields = array_merge($this->GetRowNums(),$this->GetRowAssoc()); - break; + if ($dataEntries == 1) { + $element = $eData[0]; + } else { + $element = $eData; + } - case LDAP_BOTH: - default: - $this->fields = $this->GetRowNums(); + switch ($this->fetchMode) { + case LDAP_ASSOC: + $rowData[$eName] = $element; + break; + case LDAP_NUM: + $rowData[$rowIndex] = $element; + break; + case LDAP_BOTH: + default: + $rowData[$eName] = $element; + $rowData[$rowIndex] = $element; + break; + } + + $this->seekData[$rowIndex] = $eName; + + $rowIndex++; + } + + if ($objectClass) { + // Append the element onto the end of the row + switch ($this->fetchMode) { + case LDAP_ASSOC: + $rowData['objectClass'] = $objectClass; + break; + case LDAP_NUM: + $rowData[$rowIndex] = $objectClass; + break; + case LDAP_BOTH: + default: + $rowData['objectClass'] = $objectClass; + $rowData[$rowIndex] = $objectClass; + break; + } + } + + switch (ADODB_ASSOC_CASE) { + case ADODB_ASSOC_CASE_UPPER: + $rowData = array_change_key_case($rowData, CASE_UPPER); + array_map('strtoupper', $this->seekData); + break; + case ADODB_ASSOC_CASE_LOWER: + $rowData = array_change_key_case($rowData, CASE_LOWER); + array_map('strtolower', $this->seekData); break; } - return is_array( $this->fields ); + $this->bind = array_flip($this->seekData); + + $this->fields = $rowData; + $this->_numOfFields = count($rowData); + + return is_array($this->fields); } - function _close() { - @ldap_free_result( $this->_queryID ); + /** + * Frees the memory from a query recordset + * + * @return void + */ + public function _close() + { + if (is_resource($this->_queryID)) { + @ldap_free_result($this->_queryID); + } + $this->_queryID = false; } diff --git a/drivers/adodb-mssql.inc.php b/drivers/adodb-mssql.inc.php index c0b714ed..c0a2eabd 100644 --- a/drivers/adodb-mssql.inc.php +++ b/drivers/adodb-mssql.inc.php @@ -857,18 +857,12 @@ class ADORecordset_mssql extends ADORecordSet { var $hasFetchAssoc; // see PHPLens Issue No: 6083 // _mths works only in non-localised system - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { + parent::__construct($queryID, $mode); + // freedts check... $this->hasFetchAssoc = function_exists('mssql_fetch_assoc'); - - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - - } - $this->fetchMode = $mode; - return parent::__construct($id); } diff --git a/drivers/adodb-mssqlnative.inc.php b/drivers/adodb-mssqlnative.inc.php index f7e1bcc1..528a7018 100644 --- a/drivers/adodb-mssqlnative.inc.php +++ b/drivers/adodb-mssqlnative.inc.php @@ -95,7 +95,9 @@ class ADODB_mssqlnative extends ADOConnection { var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000 var $uniqueOrderBy = true; var $_bindInputArray = true; - var $_dropSeqSQL = "drop table %s"; + + /** @var string SQL statement to drop a Sequence. */ + var $_dropSeqSQL = "DROP SEQUENCE %s"; var $connectionInfo = array('ReturnDatesAsStrings'=>true); var $cachedSchemaFlush = false; @@ -136,26 +138,12 @@ class ADODB_mssqlnative extends ADOConnection { $this->mssql_version = $version; } - function ServerInfo() { - global $ADODB_FETCH_MODE; - static $arr = false; - if (is_array($arr)) - return $arr; - if ($this->fetchMode === false) { - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - } elseif ($this->fetchMode >=0 && $this->fetchMode <=2) { - $savem = $this->fetchMode; - } else - $savem = $this->SetFetchMode(ADODB_FETCH_NUM); - - $arrServerInfo = sqlsrv_server_info($this->_connectionID); - $ADODB_FETCH_MODE = $savem; - - $arr = array(); - $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase']; - $arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']); - return $arr; + function serverInfo() { + $info = sqlsrv_server_info($this->_connectionID); + return array( + 'description' => $info['SQLServerName'] . ' connected to ' . $info['CurrentDatabase'], + 'version' => $info['SQLServerVersion'], + ); } function IfNull( $field, $ifNull ) @@ -217,7 +205,15 @@ class ADODB_mssqlnative extends ADOConnection { /** * For Server 2005,2008, duplicate a sequence with an identity table - */ + * + * + * @param string $seq The name of the sequence + * @param int $start the start number of the sequence + * + * @return bool|object + * + * @deprecated + */ function CreateSequence2008($seq='adodbseq',$start=1) { if($this->debug) ADOConnection::outp("<hr>CreateSequence($seq,$start)"); @@ -231,25 +227,42 @@ class ADODB_mssqlnative extends ADOConnection { return false; } sqlsrv_commit($this->_connectionID); - return true; + return $ok; } /** * Proper Sequences Only available to Server 2012 and up + * + * @param string $seq The name of the sequence + * @param int $start the start number of the sequence + * + * @return bool|object */ - function CreateSequence2012($seq='adodbseq',$start=1){ + function CreateSequence2012($seq='adodbseq',$start=1) { + if (!$this->sequences){ $sql = "SELECT name FROM sys.sequences"; $this->sequences = $this->GetCol($sql); } - $ok = $this->Execute("CREATE SEQUENCE $seq START WITH $start INCREMENT BY 1"); - if (!$ok) - die("CANNOT CREATE SEQUENCE" . print_r(sqlsrv_errors(),true)); + + $sql = "CREATE SEQUENCE $seq START WITH $start INCREMENT BY 1"; + $ok = $this->execute($sql); + + if (!$ok) { + $creationErrors = print_r(sqlsrv_errors(), true); + ADOConnection::outp("Error creating sequence:" . $creationErrors); + return false; + } + $this->sequences[] = $seq; + + return new ADORecordSet_empty; } /** * For Server 2005,2008, duplicate a sequence with an identity table + * + * @deprecated */ function GenID2008($seq='adodbseq',$start=1) { @@ -275,6 +288,11 @@ class ADODB_mssqlnative extends ADOConnection { * Cannot do this the normal adodb way by trapping an error if the * sequence does not exist because sql server will auto create a * sequence with the starting number of -9223372036854775808 + * + * @param string $seq The sequence name + * @param int $start The start number for the sequence + * + * @return int */ function GenID2012($seq='adodbseq',$start=1) { @@ -286,17 +304,47 @@ class ADODB_mssqlnative extends ADOConnection { * we need access to at least 1. If we really care about * performance, we could maybe flag a 'nocheck' class variable */ - if (!$this->sequences){ + if (!$this->sequences) { $sql = "SELECT name FROM sys.sequences"; $this->sequences = $this->GetCol($sql); } if (!is_array($this->sequences) - || is_array($this->sequences) && !in_array($seq,$this->sequences)){ + || is_array($this->sequences) && !in_array($seq, $this->sequences)){ $this->CreateSequence2012($seq, $start); } $num = $this->GetOne("SELECT NEXT VALUE FOR $seq"); - return $num; + return (int)$num; + } + + /** + * Drops a sequence + * Overrides the default because SQL Server returns + * a resource for the drop which confuses execute() + * + * @param string $seqname + * + * @return bool|ADORecordset_empty + */ + public function dropSequence($seqname='adodbseq') { + + if (!$seqname) { + return false; + } + + $success = $this->_query(sprintf($this->_dropSeqSQL,$seqname)); + + if ($success) { + /* + * Remove from the sequence list + */ + $index = array_search($seqname,$this->sequences); + if ($index !== false) { + unset($this->sequences[$index]); + } + return new ADORecordSet_empty; + } + return false; } // Format date column in sql string given an input format that understands Y M D @@ -1053,15 +1101,6 @@ class ADORecordset_mssqlnative extends ADORecordSet { var $fieldOffset = 0; // _mths works only in non-localised system - /** - * @var bool True if we have retrieved the fields metadata - */ - private $fieldObjectsRetrieved = false; - - /* - * Cross-reference the objects by name for easy access - */ - private $fieldObjectsIndex = array(); /* * Cross references the dateTime objects for faster decoding @@ -1107,20 +1146,6 @@ class ADORecordset_mssqlnative extends ADORecordSet { ); - - - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - - } - $this->fetchMode = $mode; - parent::__construct($id); - } - - function _initrs() { $this->_numOfRows = -1;//not supported diff --git a/drivers/adodb-mysqli.inc.php b/drivers/adodb-mysqli.inc.php index a6678274..dd992434 100644 --- a/drivers/adodb-mysqli.inc.php +++ b/drivers/adodb-mysqli.inc.php @@ -22,6 +22,9 @@ * * @copyright 2000-2013 John Lim * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community + * + * @noinspection PhpComposerExtensionStubsInspection, SqlNoDataSourceInspection + * @noinspection PhpMissingFieldTypeInspection, PhpMissingParamTypeInspection, PhpMissingReturnTypeInspection */ // security - hide paths @@ -44,7 +47,8 @@ class ADODB_mysqli extends ADOConnection { var $dataProvider = 'mysql'; var $hasInsertID = true; var $hasAffectedRows = true; - var $metaTablesSQL = "SELECT + var $metaTablesSQL = /** @lang text */ + "SELECT TABLE_NAME, CASE WHEN TABLE_TYPE = 'VIEW' THEN 'V' ELSE 'T' END FROM INFORMATION_SCHEMA.TABLES @@ -55,8 +59,8 @@ class ADODB_mysqli extends ADOConnection { var $hasMoveFirst = true; var $hasGenID = true; var $isoDates = true; // accepts dates in ISO format - var $sysDate = 'CURDATE()'; - var $sysTimeStamp = 'NOW()'; + var $sysDate = '(CURDATE())'; + var $sysTimeStamp = '(NOW())'; var $hasTransactions = true; var $forceNewConnect = false; var $poorAffectedRows = true; @@ -66,7 +70,6 @@ class ADODB_mysqli extends ADOConnection { var $socket = ''; //Default to empty string to fix HHVM bug var $_bindInputArray = false; var $nameQuote = '`'; /// string to use to quote identifiers and names - var $optionFlags = array(array(MYSQLI_READ_DEFAULT_GROUP,0)); var $arrayClass = 'ADORecordSet_array_mysqli'; var $multiQuery = false; var $ssl_key = null; @@ -100,9 +103,13 @@ class ADODB_mysqli extends ADOConnection { /** * Tells the insert_id method how to obtain the last value, depending on whether * we are using a stored procedure or not + * @var bool + * @noinspection PhpPropertyOnlyWrittenInspection */ private $usePreparedStatement = false; private $useLastInsertStatement = false; + + /** @noinspection PhpPropertyOnlyWrittenInspection */ private $usingBoundVariables = false; private $statementAffectedRows = -1; @@ -218,21 +225,21 @@ class ADODB_mysqli extends ADOConnection { /** * 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 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. + *@todo add: parameter int $port, parameter string $socket + * */ function _connect($argHostname = null, $argUsername = null, $argPassword = null, - $argDatabasename = null, + $argDatabaseName = null, $persist = false) { if(!extension_loaded("mysqli")) { @@ -255,15 +262,6 @@ class ADODB_mysqli extends ADOConnection { } return false; } - /* - I suggest a simple fix which would enable adodb and mysqli driver to - 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]); - } // Now merge in the standard connection parameters setting foreach ($this->connectionParameters as $options) { @@ -295,20 +293,18 @@ class ADODB_mysqli extends ADOConnection { } } - #if (!empty($this->port)) $argHostname .= ":".$this->port; - /** @noinspection PhpCastIsUnnecessaryInspection */ $ok = @mysqli_real_connect($this->_connectionID, $argHostname, $argUsername, $argPassword, - $argDatabasename, + $argDatabaseName, # PHP7 compat: port must be int. Use default port if cast yields zero (int)$this->port != 0 ? (int)$this->port : 3306, $this->socket, $this->clientFlags); if ($ok) { - if ($argDatabasename) return $this->selectDB($argDatabasename); + if ($argDatabaseName) return $this->selectDB($argDatabaseName); return true; } else { if ($this->debug) { @@ -325,14 +321,14 @@ class ADODB_mysqli extends ADOConnection { * @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. + * @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) + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) { - return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true); + return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true); } /** @@ -565,7 +561,7 @@ class ADODB_mysqli extends ADOConnection { } // Reference on Last_Insert_ID on the recommended way to simulate sequences - var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; + var $_genIDSQL = /** @lang text */ "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)"; @@ -688,7 +684,7 @@ class ADODB_mysqli extends ADOConnection { // parse index data into array while ($row = $rs->fetchRow()) { - if ($primary == FALSE AND $row[2] == 'PRIMARY') { + if (!$primary AND $row[2] == 'PRIMARY') { continue; } @@ -831,8 +827,6 @@ class ADODB_mysqli extends ADOConnection { $fraction = $dayFraction * 24 * 3600; return $date . ' + INTERVAL ' . $fraction.' SECOND'; - -// return "from_unixtime(unix_timestamp($date)+$fraction)"; } /** @@ -953,15 +947,27 @@ class ADODB_mysqli extends ADOConnection { $table = "$owner.$table"; } - $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE `%s`', $table)); + $a_create_table = array_change_key_case( + $this->getRow( + sprintf('SHOW CREATE TABLE `%s`', $table)), + CASE_UPPER + ); $this->setFetchMode($savem); - $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"]; + $create_sql = $a_create_table["CREATE TABLE"] ?? $a_create_table["CREATE VIEW"]; $matches = array(); - if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false; + if (!preg_match_all( + "/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", + $create_sql, + $matches + ) + ) { + return false; + } + $foreign_keys = array(); $num_keys = count($matches[0]); for ( $i = 0; $i < $num_keys; $i ++ ) { @@ -982,7 +988,7 @@ class ADODB_mysqli extends ADOConnection { if ( $associative ) { $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j]; } else { - $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}"; + $foreign_keys[$ref_table][] = $my_field[$j] . '=' . $ref_field[$j]; } } } @@ -1000,9 +1006,8 @@ class ADODB_mysqli extends ADOConnection { */ function MetaColumns($table, $normalize = true) { - $false = false; if (!$this->metaColumnsSQL) - return $false; + return false; global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; @@ -1016,7 +1021,7 @@ class ADODB_mysqli extends ADOConnection { $SQL = "SELECT column_name, column_type FROM information_schema.columns - WHERE table_schema='{$this->database}' + WHERE table_schema='$this->database' AND table_name='$table'"; $schemaArray = $this->getAssoc($SQL); @@ -1028,7 +1033,7 @@ class ADODB_mysqli extends ADOConnection { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!is_object($rs)) - return $false; + return false; $retarr = array(); while (!$rs->EOF) { @@ -1101,7 +1106,6 @@ class ADODB_mysqli extends ADOConnection { */ function SelectDB($dbName) { -// $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID); $this->database = $dbName; if ($this->_connectionID) { @@ -1160,21 +1164,11 @@ class ADODB_mysqli extends ADOConnection { */ function Prepare($sql) { - /* - * Flag the insert_id method to use the correct retrieval method - */ + // Flag the insert_id method to use the correct retrieval method $this->usePreparedStatement = true; - /* - * Prepared statements are not yet handled correctly - */ + // Prepared statements are not yet handled correctly return $sql; - $stmt = $this->_connectionID->prepare($sql); - if (!$stmt) { - echo $this->errorMsg(); - return $sql; - } - return array($sql,$stmt); } public function execute($sql, $inputarr = false) @@ -1281,12 +1275,6 @@ class ADODB_mysqli extends ADOConnection { function _query($sql, $inputarr = false) { 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 - // return value of the stored proc (ie the number of rows affected). - // Commented out for reasons of performance. You should retrieve every recordset yourself. - // if (!mysqli_next_result($this->connection->_connectionID)) return false; // When SQL is empty, mysqli_query() throws exception on PHP 8 (#945) if (!$sql) { @@ -1296,80 +1284,39 @@ class ADODB_mysqli extends ADOConnection { return false; } - if (is_array($sql)) { - - // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but - // returns as bound variables. - - $stmt = $sql[1]; - $a = ''; - foreach($inputarr as $v) { - if (is_string($v)) $a .= 's'; - else if (is_integer($v)) $a .= 'i'; - else $a .= 'd'; - } - - /* - * set prepared statement flags - */ - if ($this->usePreparedStatement) - $this->useLastInsertStatement = true; - - $fnarr = array_merge( array($stmt,$a) , $inputarr); - call_user_func_array('mysqli_stmt_bind_param',$fnarr); - return mysqli_stmt_execute($stmt); - } - else if (is_string($sql) && is_array($inputarr)) - { - - /* - * This is support for true prepared queries - * with bound parameters - * - * set prepared statement flags - */ + if (is_string($sql) && is_array($inputarr)) { + // This is support for true prepared queries with bound parameters + // set prepared statement flags $this->usePreparedStatement = true; $this->usingBoundVariables = true; $bulkBindArray = array(); - if (is_array($inputarr[0])) - { + if (is_array($inputarr[0])) { $bulkBindArray = $inputarr; $inputArrayCount = count($inputarr[0]) - 1; - } - else - { + } else { $bulkBindArray[] = $inputarr; $inputArrayCount = count($inputarr) - 1; } - - /* - * Prepare the statement with the placeholders, - * prepare will fail if the statement is invalid - * so we trap and error if necessary. Note that we - * are calling MySQL prepare here, not ADOdb - */ + // Prepare the statement with the placeholders + // prepare will fail if the statement is invalid, so we trap and error if necessary. + // Note that we are calling MySQL prepare here, not ADOdb $stmt = $this->_connectionID->prepare($sql); - if ($stmt === false) - { + if ($stmt === false) { $this->outp_throw( "SQL Statement failed on preparation: " . htmlspecialchars($sql) . "'", 'Execute' ); return false; } - /* - * Make sure the number of parameters provided in the input - * array matches what the query expects. We must discount - * the first parameter which contains the data types in - * our inbound parameters - */ - $nparams = $stmt->param_count; - if ($nparams != $inputArrayCount) - { + // Make sure the number of parameters provided in the input array + // matches what the query expects. We must discount the first + // parameter which contains the data types in our inbound parameters + $nparams = $stmt->param_count; + if ($nparams != $inputArrayCount) { $this->outp_throw( "Input array has " . $inputArrayCount . " params, does not match query: '" . htmlspecialchars($sql) . "'", @@ -1378,37 +1325,28 @@ class ADODB_mysqli extends ADOConnection { return false; } - foreach ($bulkBindArray as $inputarr) - { - /* - * Must pass references into call_user_func_array - */ + foreach ($bulkBindArray as $inputarr) { + // Must pass references into call_user_func_array $paramsByReference = array(); - foreach($inputarr as $key => $value) { + foreach ($inputarr as $key => $value) { /** @noinspection PhpArrayAccessCanBeReplacedWithForeachValueInspection */ $paramsByReference[$key] = &$inputarr[$key]; } - /* - * Bind the params - */ + // Bind the params call_user_func_array(array($stmt, 'bind_param'), $paramsByReference); - /* - * Execute - */ - + // Execute $ret = mysqli_stmt_execute($stmt); // Store error code and message $this->_errorCode = $stmt->errno; $this->_errorMsg = $stmt->error; - /* - * Did we throw an error? - */ - if ($ret == false) + // Did we throw an error? + if (!$ret) { return false; + } } // Tells affected_rows to be compliant @@ -1420,14 +1358,9 @@ class ADODB_mysqli extends ADOConnection { // Turn the statement into a result set and return it return $stmt->get_result(); - } - else - { - /* - * reset prepared statement flags, in case we set them - * previously and didn't use them - */ - $this->usePreparedStatement = false; + } else { + // Reset prepared statement flags, in case we set them previously and didn't use them + $this->usePreparedStatement = false; $this->useLastInsertStatement = false; // Reset error code and message @@ -1435,19 +1368,12 @@ class ADODB_mysqli extends ADOConnection { $this->_errorMsg = ''; } - /* - 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()); - return false; - } - - return $mysql_res; - */ - if ($this->multiQuery) { - $rs = mysqli_multi_query($this->_connectionID, $sql.';'); + $rs = mysqli_multi_query($this->_connectionID, $sql . ';'); if ($rs) { - $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID ); + $rs = ($ADODB_COUNTRECS) + ? @mysqli_store_result($this->_connectionID) + : @mysqli_use_result($this->_connectionID); return $rs ?: true; // mysqli_more_results( $this->_connectionID ) } } else { @@ -1458,11 +1384,10 @@ class ADODB_mysqli extends ADOConnection { } } - if($this->debug) + if ($this->debug) { ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg()); - + } return false; - } /** @@ -1584,12 +1509,9 @@ class ADORecordSet_mysqli extends ADORecordSet{ function __construct($queryID, $mode = false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } + parent::__construct($queryID, $mode); - switch ($mode) { + switch ($this->adodbFetchMode) { case ADODB_FETCH_NUM: $this->fetchMode = MYSQLI_NUM; break; @@ -1602,8 +1524,6 @@ class ADORecordSet_mysqli extends ADORecordSet{ $this->fetchMode = MYSQLI_BOTH; break; } - $this->adodbFetchMode = $mode; - parent::__construct($queryID); } function _initrs() @@ -1645,9 +1565,15 @@ class ADORecordSet_mysqli extends ADORecordSet{ */ function FetchField($fieldOffset = -1) { + if ($fieldOffset < -1 || $fieldOffset >= $this->_numOfFields) { + if ($this->connection->debug) { + ADOConnection::outp("FetchField: field offset out of range: $fieldOffset"); + } + return false; + } $fieldnr = $fieldOffset; if ($fieldOffset != -1) { - $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr); + @mysqli_field_seek($this->_queryID, $fieldnr); } $o = @mysqli_fetch_field($this->_queryID); if (!$o) return false; @@ -1860,16 +1786,16 @@ class ADORecordSet_mysqli extends ADORecordSet{ * * @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) + * @param bool|object $fieldObj (Optional) * * @return string The MetaType */ - function metaType($t, $len = -1, $fieldobj = false) + function metaType($t, $len = -1, $fieldObj = false) { if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; + $fieldObj = $t; + $t = $fieldObj->type; + $len = $fieldObj->max_length; } $t = strtoupper($t); @@ -1895,7 +1821,8 @@ class ADORecordSet_mysqli extends ADORecordSet{ case MYSQLI_TYPE_STRING : case MYSQLI_TYPE_ENUM : case MYSQLI_TYPE_SET : - case 253 : + /** @noinspection PhpMissingBreakStatementInspection */ + case MYSQLI_TYPE_VAR_STRING : if ($len <= $this->blobSize) { return 'C'; } @@ -1915,7 +1842,7 @@ class ADORecordSet_mysqli extends ADORecordSet{ case MYSQLI_TYPE_BLOB : case MYSQLI_TYPE_LONG_BLOB : case MYSQLI_TYPE_MEDIUM_BLOB : - return !empty($fieldobj->binary) ? 'B' : 'X'; + return !empty($fieldObj->binary) ? 'B' : 'X'; case 'YEAR': case 'DATE': @@ -1945,7 +1872,7 @@ class ADORecordSet_mysqli extends ADORecordSet{ case MYSQLI_TYPE_LONGLONG : case MYSQLI_TYPE_SHORT : case MYSQLI_TYPE_TINY : - if (!empty($fieldobj->primary_key)) { + if (!empty($fieldObj->primary_key)) { return 'R'; } return 'I'; @@ -1979,16 +1906,16 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array * * @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) + * @param bool|object $fieldObj (Optional) * * @return string The MetaType */ - function MetaType($t, $len = -1, $fieldobj = false) + function MetaType($t, $len = -1, $fieldObj = false) { if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; + $fieldObj = $t; + $t = $fieldObj->type; + $len = $fieldObj->max_length; } $t = strtoupper($t); @@ -2012,7 +1939,8 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array case MYSQLI_TYPE_STRING : case MYSQLI_TYPE_ENUM : case MYSQLI_TYPE_SET : - case 253 : + /** @noinspection PhpMissingBreakStatementInspection */ + case MYSQLI_TYPE_VAR_STRING : if ($len <= $this->blobSize) { return 'C'; } @@ -2032,7 +1960,7 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array case MYSQLI_TYPE_BLOB : case MYSQLI_TYPE_LONG_BLOB : case MYSQLI_TYPE_MEDIUM_BLOB : - return !empty($fieldobj->binary) ? 'B' : 'X'; + return !empty($fieldObj->binary) ? 'B' : 'X'; case 'YEAR': case 'DATE': @@ -2062,7 +1990,7 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array case MYSQLI_TYPE_LONGLONG : case MYSQLI_TYPE_SHORT : case MYSQLI_TYPE_TINY : - if (!empty($fieldobj->primary_key)) { + if (!empty($fieldObj->primary_key)) { return 'R'; } return 'I'; diff --git a/drivers/adodb-netezza.inc.php b/drivers/adodb-netezza.inc.php index dc7c58e4..76858909 100644 --- a/drivers/adodb-netezza.inc.php +++ b/drivers/adodb-netezza.inc.php @@ -61,7 +61,7 @@ class ADODB_netezza extends ADODB_postgres64 { var $fmtTimeStamp = "'Y-m-d G:i:s'"; // used by DBTimeStamp as the default timestamp fmt. var $ansiOuter = true; var $autoRollback = true; // apparently pgsql does not autorollback properly before 4.3.4 - // http://bugs.php.net/bug.php?id=25404 + // https://bugs.php.net/bug.php?id=25404 function MetaColumns($table,$upper=true) diff --git a/drivers/adodb-oci8.inc.php b/drivers/adodb-oci8.inc.php index b92281c7..f08700fd 100644 --- a/drivers/adodb-oci8.inc.php +++ b/drivers/adodb-oci8.inc.php @@ -69,13 +69,23 @@ class ADODB_oci8 extends ADOConnection { var $_stmt; var $_commit = OCI_COMMIT_ON_SUCCESS; var $_initdate = true; // init date to YYYY-MM-DD - var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW') and table_name not like 'BIN\$%'"; // bin$ tables are recycle bin tables - var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net - var $metaColumnsSQL2 = "select column_name,data_type,data_length, data_scale, data_precision, - case when nullable = 'Y' then 'NULL' - else 'NOT NULL' end as nulls, - data_default from all_tab_cols - where owner='%s' and table_name='%s' order by column_id"; // when there is a schema + var $metaTablesSQL = <<<ENDSQL + SELECT table_name, table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; + var $metaColumnsSQL2 = <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM all_tab_columns + WHERE owner = '%s' AND table_name = '%s' + ORDER BY column_id + ENDSQL; // When there is a schema var $_bindInputArray = true; var $hasGenID = true; var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL"; @@ -95,7 +105,7 @@ END; var $_bind = array(); var $_nestedSQL = true; var $_getarray = false; // currently not working - var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER + var $leftOuter = ''; // oracle weirdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER var $session_sharing_force_blob = false; // alter session on updateblob if set to true var $firstrows = true; // enable first rows optimization on SelectLimit() var $selectOffsetAlg1 = 1000; // when to use 1st algorithm of selectlimit. @@ -105,8 +115,6 @@ END; var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true) var $_refLOBs = array(); - // var $ansiOuter = true; // if oracle9 - /* * Legacy compatibility for sequence names for emulated auto-increments */ @@ -163,7 +171,7 @@ END; } $fld->max_length = $rs->fields[4]; } - $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); + $fld->not_null = $rs->fields[5] == 'N'; $fld->binary = (strpos($fld->type,'BLOB') !== false); $fld->default_value = $rs->fields[6]; @@ -193,21 +201,33 @@ END; } /** + * Connect to database. + * * Multiple modes of connection are supported: * - * a. Local Database - * $conn->Connect(false,'scott','tiger'); + * 1. Local Database + * $conn->connect(false, 'scott', 'tiger'); * - * b. From tnsnames.ora - * $conn->Connect($tnsname,'scott','tiger'); - * $conn->Connect(false,'scott','tiger',$tnsname); + * 2. From tnsnames.ora + * $conn->connect($tnsname, 'scott', 'tiger'); + * OR + * $conn->connect(false, 'scott', 'tiger', $tnsname); * - * c. Server + service name - * $conn->Connect($serveraddress,'scott,'tiger',$service_name); + * 3. Server + service name + * $conn->connect($serveraddress, 'scott, 'tiger', $service_name); * - * d. Server + SID + * 4. Server + SID * $conn->connectSID = true; * $conn->Connect($serveraddress,'scott,'tiger',$SID); + * OR + * $conn->Connect($serveraddress,'scott,'tiger',"SID=$SID"); + * + * By default, the Session Mode will be set to OCI_DEFAULT, but it is + * possible to use one of other modes referenced in the + * {@link https://www.php.net/manual/en/function.oci-connect oci8_connect()} + * function's documentation, like this: + * $conn->setConnectionParameter('session_mode', OCI_SYSDBA); + * $conn->connect(...); * * @param string|false $argHostname DB server hostname or TNS name * @param string $argUsername @@ -245,45 +265,60 @@ END; $argDatabasename = substr($argDatabasename,4); $this->connectSID = true; } - - if ($this->connectSID) { - $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname - .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))"; - } else - $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname - .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))"; + $sidOrService = $this->connectSID ? 'SID' : 'SERVICE_NAME'; + $argDatabasename = "(DESCRIPTION=" + . "(ADDRESS=(PROTOCOL=TCP)(HOST=$argHostname)(PORT=$argHostport))" + . "(CONNECT_DATA=($sidOrService=$argDatabasename)))"; } } - //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>"; - if ($mode==1) { - $this->_connectionID = ($this->charSet) - ? oci_pconnect($argUsername,$argPassword, $argDatabasename,$this->charSet) - : oci_pconnect($argUsername,$argPassword, $argDatabasename); - if ($this->_connectionID && $this->autoRollback) { - oci_rollback($this->_connectionID); + // Determine the connect function to use based on connection mode + switch ($mode) { + case 1: $ociConnectFunction = 'oci_pconnect'; break; + case 2: $ociConnectFunction = 'oci_new_connect'; break; + default: $ociConnectFunction = 'oci_connect'; + } + + // Process the connection parameters + $sessionMode = OCI_DEFAULT; + $clientIdentifier = ''; + foreach ($this->connectionParameters as $options) { + foreach($options as $parameter => $value) { + switch ($parameter) { + case 'session_mode': + $sessionMode = $value; + break; + case 'client_identifier': + $clientIdentifier = $value; + break; + } } - } else if ($mode==2) { - $this->_connectionID = ($this->charSet) - ? oci_new_connect($argUsername,$argPassword, $argDatabasename,$this->charSet) - : oci_new_connect($argUsername,$argPassword, $argDatabasename); - } else { - $this->_connectionID = ($this->charSet) - ? oci_connect($argUsername,$argPassword, $argDatabasename,$this->charSet) - : oci_connect($argUsername,$argPassword, $argDatabasename); } + + $this->_connectionID = $ociConnectFunction( + $argUsername, + $argPassword, + $argDatabasename, + $this->charSet ?: '', + $sessionMode + ); if (!$this->_connectionID) { return false; } + // Set client identifier, but see documentation for limitations + if ($clientIdentifier) { + oci_set_client_identifier($this->_connectionID, $clientIdentifier); + } + + if ($mode == 1 && $this->autoRollback ) { + oci_rollback($this->_connectionID); + } + if ($this->_initdate) { $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'"); } - // looks like: - // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production - // $vers = oci_server_version($this->_connectionID); - // if (strpos($vers,'8i') !== false) $this->ansiOuter = true; return true; } @@ -371,7 +406,7 @@ END; $ds = $d->format($this->fmtDate); } else { - $ds = adodb_date($this->fmtDate,$d); + $ds = date($this->fmtDate,$d); } return "TO_DATE(".$ds.",'".$this->dateformat."')"; @@ -400,7 +435,7 @@ END; $tss = $ts->format("'Y-m-d H:i:s'"); } else { - $tss = adodb_date("'Y-m-d H:i:s'",$ts); + $tss = date("'Y-m-d H:i:s'",$ts); } return $tss; @@ -547,7 +582,7 @@ END; $ok = true; } - return $ok ? true : false; + return (bool)$ok; } function CommitTrans($ok=true) @@ -1252,9 +1287,9 @@ END; * $db->Parameter($stmt,$group,'group'); * $db->Execute($stmt); * - * @param $stmt Statement returned by {@see Prepare()} or {@see PrepareSP()}. - * @param $var PHP variable to bind to - * @param $name Name of stored procedure variable name to bind to. + * @param array $stmt Statement returned by {@see Prepare()} or {@see PrepareSP()}. + * @param mixed $var PHP variable to bind to + * @param string $name Name of stored procedure variable name to bind to. * @param bool $isOutput Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. * @param int $maxLen Holds an maximum length of the variable. * @param mixed $type The data type of $var. Legal values depend on driver. @@ -1398,15 +1433,13 @@ END; $ok = oci_execute($cursor); return $cursor; } - return $stmt; } else { if (is_resource($stmt)) { oci_free_statement($stmt); return true; } - return $stmt; } - break; + return $stmt; default : return true; @@ -1571,8 +1604,6 @@ SELECT /*+ RULE */ distinct b.column_name * It remains for backwards compatibility. * * @return string Quoted string to be sent back to database - * - * @noinspection PhpUnusedParameterInspection */ function qStr($s, $magic_quotes=false) { @@ -1600,13 +1631,11 @@ class ADORecordset_oci8 extends ADORecordSet { /** @var resource Cursor reference */ var $_refcursor; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) { + parent::__construct($queryID, $mode); + + switch ($this->adodbFetchMode) { case ADODB_FETCH_ASSOC: $this->fetchMode = OCI_ASSOC; break; @@ -1620,8 +1649,6 @@ class ADORecordset_oci8 extends ADORecordSet { break; } $this->fetchMode += OCI_RETURN_NULLS + OCI_RETURN_LOBS; - $this->adodbFetchMode = $mode; - $this->_queryID = $queryID; } /** @@ -1821,7 +1848,7 @@ class ADORecordset_oci8 extends ADORecordSet { * @param mixed $t * @param int $len [optional] Length of blobsize * @param bool $fieldobj [optional][discarded] - * @return str The metatype of the field + * @return string The metatype of the field */ function MetaType($t, $len=-1, $fieldobj=false) { @@ -1876,11 +1903,3 @@ class ADORecordset_oci8 extends ADORecordSet { } } } - -class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 { - - function MoveNext() - { - return adodb_movenext($this); - } -} diff --git a/drivers/adodb-oci8po.inc.php b/drivers/adodb-oci8po.inc.php index 71c203b6..3ecebebc 100644 --- a/drivers/adodb-oci8po.inc.php +++ b/drivers/adodb-oci8po.inc.php @@ -32,8 +32,17 @@ include_once(ADODB_DIR.'/drivers/adodb-oci8.inc.php'); class ADODB_oci8po extends ADODB_oci8 { var $databaseType = 'oci8po'; var $dataProvider = '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')"; + var $metaTablesSQL = <<<ENDSQL + SELECT lower(table_name), table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = <<<ENDSQL + SELECT lower(column_name), data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; function Param($name,$type='C') { diff --git a/drivers/adodb-odbc.inc.php b/drivers/adodb-odbc.inc.php index 13184d07..a52d8b02 100644 --- a/drivers/adodb-odbc.inc.php +++ b/drivers/adodb-odbc.inc.php @@ -613,20 +613,13 @@ class ADORecordSet_odbc extends ADORecordSet { var $dataProvider = "odbc"; var $useFetchArray; - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $id; + parent::__construct($queryID, $mode); // the following is required for mysql odbc driver in 4.3.1 -- why? $this->EOF = false; $this->_currentRow = -1; - //parent::__construct($id); } diff --git a/drivers/adodb-odbc_oracle.inc.php b/drivers/adodb-odbc_oracle.inc.php index d8461e1a..8c608295 100644 --- a/drivers/adodb-odbc_oracle.inc.php +++ b/drivers/adodb-odbc_oracle.inc.php @@ -33,8 +33,17 @@ class ADODB_odbc_oracle extends ADODB_odbc { var $concat_operator='||'; var $fmtDate = "'Y-m-d 00:00:00'"; var $fmtTimeStamp = "'Y-m-d h:i:sA'"; - var $metaTablesSQL = 'select table_name from cat'; - var $metaColumnsSQL = "select cname,coltype,width from col where tname='%s' order by colno"; + var $metaTablesSQL = <<<ENDSQL + SELECT table_name, table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; var $sysDate = "TRUNC(SYSDATE)"; var $sysTimeStamp = 'SYSDATE'; @@ -42,9 +51,8 @@ class ADODB_odbc_oracle extends ADODB_odbc { function MetaTables($ttype = false, $showSchema = false, $mask = false) { - $false = false; $rs = $this->Execute($this->metaTablesSQL); - if ($rs === false) return $false; + if ($rs === false) return false; $arr = $rs->GetArray(); $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { @@ -60,19 +68,20 @@ class ADODB_odbc_oracle extends ADODB_odbc { $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); if ($rs === false) { - $false = false; - return $false; + return false; } $retarr = array(); - while (!$rs->EOF) { //print_r($rs->fields); + while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; - - if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; - else $retarr[strtoupper($fld->name)] = $fld; + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) { + $retarr[] = $fld; + } else { + $retarr[strtoupper($fld->name)] = $fld; + } $rs->MoveNext(); } diff --git a/drivers/adodb-odbtp.inc.php b/drivers/adodb-odbtp.inc.php index e3f81481..46bf9c4a 100644 --- a/drivers/adodb-odbtp.inc.php +++ b/drivers/adodb-odbtp.inc.php @@ -81,7 +81,7 @@ class ADODB_odbtp extends ADOConnection{ if ($isfld) return "convert(date, $d, 120)"; if (is_string($d)) $d = ADORecordSet::UnixDate($d); - $d = adodb_date($this->fmtDate,$d); + $d = date($this->fmtDate,$d); return "convert(date, $d, 120)"; } @@ -91,7 +91,7 @@ class ADODB_odbtp extends ADOConnection{ if ($isfld) return "convert(datetime, $d, 120)"; if (is_string($d)) $d = ADORecordSet::UnixDate($d); - $d = adodb_date($this->fmtDate,$d); + $d = date($this->fmtDate,$d); return "convert(datetime, $d, 120)"; } */ @@ -684,15 +684,6 @@ class ADORecordSet_odbtp extends ADORecordSet { var $databaseType = 'odbtp'; var $canSeek = true; - function __construct($queryID,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($queryID); - } function _initrs() { diff --git a/drivers/adodb-oracle.inc.php b/drivers/adodb-oracle.inc.php index dc0462fe..7c061b07 100644 --- a/drivers/adodb-oracle.inc.php +++ b/drivers/adodb-oracle.inc.php @@ -41,7 +41,7 @@ class ADODB_oracle extends ADOConnection { { if (is_string($d)) $d = ADORecordSet::UnixDate($d); if (is_object($d)) $ds = $d->format($this->fmtDate); - else $ds = adodb_date($this->fmtDate,$d); + else $ds = date($this->fmtDate,$d); return 'TO_DATE('.$ds.",'YYYY-MM-DD')"; } @@ -51,7 +51,7 @@ class ADODB_oracle extends ADOConnection { if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts); if (is_object($ts)) $ds = $ts->format($this->fmtDate); - else $ds = adodb_date($this->fmtTimeStamp,$ts); + else $ds = date($this->fmtTimeStamp,$ts); return 'TO_DATE('.$ds.",'RRRR-MM-DD, HH:MI:SS AM')"; } @@ -223,16 +223,9 @@ class ADORecordset_oracle extends ADORecordSet { var $databaseType = "oracle"; var $bind = false; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $queryID; + parent::__construct($queryID, $mode); $this->_inited = true; $this->fields = array(); @@ -249,9 +242,7 @@ class ADORecordset_oracle extends ADORecordSet { return $this->_queryID; } - - - /* Returns: an object containing field information. + /* 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. */ diff --git a/drivers/adodb-pdo.inc.php b/drivers/adodb-pdo.inc.php index d254895e..f25006ba 100644 --- a/drivers/adodb-pdo.inc.php +++ b/drivers/adodb-pdo.inc.php @@ -66,6 +66,10 @@ function adodb_pdo_type($t) class ADODB_pdo extends ADOConnection { + const BIND_USE_QUESTION_MARKS = 0; + const BIND_USE_NAMED_PARAMETERS = 1; + const BIND_USE_BOTH = 2; + var $databaseType = "pdo"; var $dataProvider = "pdo"; var $fmtDate = "'Y-m-d'"; @@ -100,6 +104,15 @@ class ADODB_pdo extends ADOConnection { */ public $pdoParameters = array(); + /* + * Set which style is used to bind parameters + * + * BIND_USE_QUESTION_MARKS = Use only question marks + * BIND_USE_NAMED_PARAMETERS = Use only named parameters + * BIND_USE_BOTH = Use both question marks and named parameters (Default) + */ + public $bindParameterStyle = self::BIND_USE_BOTH; + function _UpdatePDO() { $d = $this->_driver; @@ -300,10 +313,10 @@ class ADODB_pdo extends ADOConnection { return $this->_driver->MetaColumns($table,$normalize); } - public function metaIndexes($table,$normalize=true,$owner=false) + public function metaIndexes($table, $primary=true, $owner=false) { if (method_exists($this->_driver,'metaIndexes')) - return $this->_driver->metaIndexes($table,$normalize,$owner); + return $this->_driver->metaIndexes($table, $primary, $owner); } /** @@ -592,11 +605,7 @@ class ADODB_pdo extends ADOConnection { $this->_driver->debug = $this->debug; } if ($inputarr) { - - /* - * inputarr must be numeric - */ - $inputarr = array_values($inputarr); + $inputarr = $this->conformToBindParameterStyle($stmt->queryString, $inputarr); $ok = $stmt->execute($inputarr); } else { @@ -666,6 +675,58 @@ class ADODB_pdo extends ADOConnection { return "'" . str_replace("'", $this->replaceQuote, $s) . "'"; } + /** + * Make bind parameters conform to settings. + * + * @param string $sql + * @param array $inputarr + * @return array + */ + private function conformToBindParameterStyle($sql, $inputarr) + { + switch ($this->bindParameterStyle) + { + case self::BIND_USE_QUESTION_MARKS: + default: + $inputarr = array_values($inputarr); + break; + + case self::BIND_USE_NAMED_PARAMETERS: + break; + + case self::BIND_USE_BOTH: + // inputarr must be numeric if SQL contains a question mark + if ($this->containsQuestionMarkPlaceholder($sql)) { + $inputarr = array_values($inputarr); + + if ($this->debug) { + ADOconnection::outp('improve the performance of this query by setting the bindParameterStyle to BIND_USE_QUESTION_MARKS'); + } + } + break; + } + + return $inputarr; + } + + /** + * Checks for the inclusion of a question mark placeholder. + * + * @param string $sql SQL string + * @return boolean Returns true if a question mark placeholder is included + */ + private function containsQuestionMarkPlaceholder($sql) + { + $pattern = '/(.\?(:?.|$))/'; + if (preg_match_all($pattern, $sql, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + if ($match[1] !== '`?`' && strpos($match[1], '??') === false) { + return true; + } + } + } + return false; + } } /** @@ -673,14 +734,18 @@ class ADODB_pdo extends ADOConnection { */ class ADODB_pdo_base extends ADODB_pdo { - var $sysDate = "'?'"; - var $sysTimeStamp = "'?'"; - - - function _init($parentDriver) + /** + * Initialize parent driver properties with driver-specific values. + * + * Called by {@see ADODB_pdo::_UpdatePDO()}. + * + * @param ADODB_pdo $parentDriver + * @return void + * @internal + */ + protected function _init(ADODB_pdo $parentDriver) { $parentDriver->_bindInputArray = true; - #$parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true); } function ServerInfo() @@ -792,27 +857,25 @@ class ADORecordSet_pdo extends ADORecordSet { /** @var PDOStatement */ var $_queryID; - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->adodbFetchMode = $mode; - switch($mode) { - case ADODB_FETCH_NUM: $mode = PDO::FETCH_NUM; break; - case ADODB_FETCH_ASSOC: $mode = PDO::FETCH_ASSOC; break; + parent::__construct($queryID, $mode); - case ADODB_FETCH_BOTH: - default: $mode = PDO::FETCH_BOTH; break; + switch($this->adodbFetchMode) { + case ADODB_FETCH_NUM: + $mode = PDO::FETCH_NUM; + break; + case ADODB_FETCH_ASSOC: + $mode = PDO::FETCH_ASSOC; + break; + case ADODB_FETCH_BOTH: + default: + $mode = PDO::FETCH_BOTH; + break; } $this->fetchMode = $mode; - - $this->_queryID = $id; - parent::__construct($id); } - function Init() { if ($this->_inited) { diff --git a/drivers/adodb-pdo_dblib.inc.php b/drivers/adodb-pdo_dblib.inc.php index fa217af0..c3a3554c 100644 --- a/drivers/adodb-pdo_dblib.inc.php +++ b/drivers/adodb-pdo_dblib.inc.php @@ -17,25 +17,27 @@ * * @copyright 2000-2013 John Lim * @copyright 2019 Damien Regad, Mark Newnham and the ADOdb community + * + * @noinspection PhpComposerExtensionStubsInspection */ -class ADODB_pdo_dblib extends ADODB_pdo +class ADODB_pdo_dblib extends ADODB_pdo_base { 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"; + var $metaDatabasesSQL = /** @lang text */ "select name from sysdatabases where name <> 'master'"; + var $metaTablesSQL= /** @lang text */ "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 = /** @lang text */ "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"; + var $fmtDate = "'Y-m-d'"; + var $fmtTimeStamp = "'Y-m-d H:i:s'"; - function _init(ADODB_pdo $parentDriver) + protected function _init(ADODB_pdo $parentDriver) { + parent::_init($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() diff --git a/drivers/adodb-pdo_firebird.inc.php b/drivers/adodb-pdo_firebird.inc.php index 4671b396..265cde49 100644 --- a/drivers/adodb-pdo_firebird.inc.php +++ b/drivers/adodb-pdo_firebird.inc.php @@ -19,6 +19,8 @@ * * @copyright 2000-2013 John Lim * @copyright 2019 Damien Regad, Mark Newnham and the ADOdb community + * + * @noinspection SqlResolve */ /** @@ -28,12 +30,12 @@ class ADODB_pdo_firebird extends ADODB_pdo_base { 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"; + 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"; var $arrayClass = 'ADORecordSet_array_pdo_firebird'; /** - * Gets the version iformation from the server + * Gets the version information from the server * * @return string[] */ @@ -69,9 +71,7 @@ class ADODB_pdo_firebird extends ADODB_pdo_base */ public function metaTables($ttype = false, $showSchema = false, $mask = false) { - $ret = ADOConnection::MetaTables($ttype, $showSchema); - - return $ret; + return ADOConnection::MetaTables($ttype, $showSchema); } public function metaColumns($table, $normalize = true) @@ -184,7 +184,7 @@ class ADODB_pdo_firebird extends ADODB_pdo_base 'columns' => array() ); } - $sql = "SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '" . $index . "' ORDER BY RDB\$FIELD_POSITION ASC"; + $sql = "SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '" . $index . "' ORDER BY RDB\$FIELD_POSITION"; $rs1 = $this->Execute($sql); while ($row1 = $rs1->FetchRow()) { $indexes[$index]['columns'][$row1[2]] = $row1[1]; @@ -393,15 +393,9 @@ 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) { + return false; } } diff --git a/drivers/adodb-pdo_mssql.inc.php b/drivers/adodb-pdo_mssql.inc.php index ef63b654..ad94233e 100644 --- a/drivers/adodb-pdo_mssql.inc.php +++ b/drivers/adodb-pdo_mssql.inc.php @@ -19,18 +19,17 @@ * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community */ -class ADODB_pdo_mssql extends ADODB_pdo { +class ADODB_pdo_mssql extends ADODB_pdo_base { var $hasTop = 'top'; var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)'; var $sysTimeStamp = 'GetDate()'; - function _init($parentDriver) + protected function _init(ADODB_pdo $parentDriver) { - - $parentDriver->hasTransactions = false; ## <<< BUG IN PDO mssql driver $parentDriver->_bindInputArray = false; + $parentDriver->hasTransactions = false; ## <<< BUG IN PDO mssql driver $parentDriver->hasInsertID = true; } diff --git a/drivers/adodb-pdo_mysql.inc.php b/drivers/adodb-pdo_mysql.inc.php index a00c8595..98f112c1 100644 --- a/drivers/adodb-pdo_mysql.inc.php +++ b/drivers/adodb-pdo_mysql.inc.php @@ -17,19 +17,23 @@ * * @copyright 2000-2013 John Lim * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community + * + * @noinspection PhpComposerExtensionStubsInspection */ -class ADODB_pdo_mysql extends ADODB_pdo { +class ADODB_pdo_mysql extends ADODB_pdo_base { - var $metaTablesSQL = "SELECT + var $metaTablesSQL = /** @lang text */ + "SELECT TABLE_NAME, CASE WHEN TABLE_TYPE = 'VIEW' THEN 'V' ELSE 'T' END FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="; var $metaColumnsSQL = "SHOW COLUMNS FROM `%s`"; - var $sysDate = 'CURDATE()'; - var $sysTimeStamp = 'NOW()'; + var $sysDate = '(CURDATE())'; + var $sysTimeStamp = '(NOW())'; var $hasGenID = true; + /** @noinspection SqlWithoutWhere */ 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"; @@ -38,10 +42,9 @@ class ADODB_pdo_mysql extends ADODB_pdo { var $fmtTimeStamp = "'Y-m-d H:i:s'"; var $nameQuote = '`'; - function _init($parentDriver) + protected function _init(ADODB_pdo $parentDriver) { $parentDriver->hasTransactions = false; - #$parentDriver->_bindInputArray = false; $parentDriver->hasInsertID = true; $parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); } @@ -158,14 +161,14 @@ class ADODB_pdo_mysql extends ADODB_pdo { return $ret; } - /** - * @param bool $auto_commit - * @return void - */ - function SetAutoCommit($auto_commit) - { - $this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT, $auto_commit); - } + /** + * @param bool $auto_commit + * @return void + */ + function SetAutoCommit($auto_commit) + { + $this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT, $auto_commit); + } function SetTransactionMode($transaction_mode) { diff --git a/drivers/adodb-pdo_oci.inc.php b/drivers/adodb-pdo_oci.inc.php index 20b47def..d7874b7c 100644 --- a/drivers/adodb-pdo_oci.inc.php +++ b/drivers/adodb-pdo_oci.inc.php @@ -17,6 +17,8 @@ * * @copyright 2000-2013 John Lim * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community + * + * @noinspection PhpComposerExtensionStubsInspection */ class ADODB_pdo_oci extends ADODB_pdo_base { @@ -26,15 +28,25 @@ class ADODB_pdo_oci extends ADODB_pdo_base { var $sysTimeStamp = 'SYSDATE'; var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS' var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)"; - var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW')"; - var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; + var $metaTablesSQL = /** @lang text */ <<<ENDSQL + SELECT table_name, table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = /** @lang text */ <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; var $_initdate = true; var $_hasdual = true; - function _init($parentDriver) + protected function _init(ADODB_pdo $parentDriver) { - $parentDriver->_bindInputArray = true; + parent::_init($parentDriver); + $parentDriver->_nestedSQL = true; if ($this->_initdate) { $parentDriver->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'"); @@ -58,9 +70,8 @@ class ADODB_pdo_oci extends ADODB_pdo_base { function MetaColumns($table,$normalize=true) { - global $ADODB_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); @@ -70,20 +81,22 @@ class ADODB_pdo_oci extends ADODB_pdo_base { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs) { - return $false; + return false; } $retarr = array(); - while (!$rs->EOF) { //print_r($rs->fields); + while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; $fld->scale = $rs->fields[3]; - if ($rs->fields[1] == 'NUMBER' && $rs->fields[3] == 0) { - $fld->type ='INT'; + if ($rs->fields[1] == 'NUMBER') { + if ($rs->fields[3] == 0) { + $fld->type = 'INT'; + } $fld->max_length = $rs->fields[4]; } - $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); + $fld->not_null = $rs->fields[5] == 'N'; $fld->binary = (strpos($fld->type,'BLOB') !== false); $fld->default_value = $rs->fields[6]; @@ -93,7 +106,7 @@ class ADODB_pdo_oci extends ADODB_pdo_base { } $rs->Close(); if (empty($retarr)) - return $false; + return false; else return $retarr; } diff --git a/drivers/adodb-pdo_pgsql.inc.php b/drivers/adodb-pdo_pgsql.inc.php index 6c06ac10..bea00e15 100644 --- a/drivers/adodb-pdo_pgsql.inc.php +++ b/drivers/adodb-pdo_pgsql.inc.php @@ -19,7 +19,10 @@ * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community */ -class ADODB_pdo_pgsql extends ADODB_pdo { +/** + * @noinspection SqlResolve + */ +class ADODB_pdo_pgsql extends ADODB_pdo_base { var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1"; var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages', @@ -64,8 +67,9 @@ class ADODB_pdo_pgsql extends ADODB_pdo { var $random = 'random()'; /// random function var $concat_operator='||'; - function _init($parentDriver) + protected function _init(ADODB_pdo $parentDriver) { + parent::_init($parentDriver); $parentDriver->hasTransactions = false; ## <<< BUG IN PDO pgsql driver $parentDriver->hasInsertID = true; @@ -147,8 +151,7 @@ select viewname,'V' from pg_views where viewname like $mask"; $ADODB_FETCH_MODE = $save; if ($rs === false) { - $false = false; - return $false; + return false; } if (!empty($this->metaKeySQL)) { // If we want the primary keys, we have to issue a separate query @@ -180,7 +183,8 @@ select viewname,'V' from pg_views where viewname like $mask"; while (!$rsdef->EOF) { $num = $rsdef->fields['num']; $s = $rsdef->fields['def']; - if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ + if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { + // FIXME: quoted strings hack... for now... $s = substr($s, 1); $s = substr($s, 0, strlen($s) - 1); } @@ -235,8 +239,7 @@ select viewname,'V' from pg_views where viewname like $mask"; } $rs->Close(); if (empty($retarr)) { - $false = false; - return $false; + return false; } else return $retarr; } diff --git a/drivers/adodb-pdo_sqlite.inc.php b/drivers/adodb-pdo_sqlite.inc.php index dab1de99..f2f5653f 100644 --- a/drivers/adodb-pdo_sqlite.inc.php +++ b/drivers/adodb-pdo_sqlite.inc.php @@ -21,7 +21,8 @@ * @author Sid Dunayer <sdunayer@interserv.com> */ -class ADODB_pdo_sqlite extends ADODB_pdo { +class ADODB_pdo_sqlite extends ADODB_pdo_base { + /** @noinspection SqlResolve */ var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table'"; var $sysDate = 'current_date'; var $sysTimeStamp = 'current_timestamp'; @@ -37,11 +38,13 @@ class ADODB_pdo_sqlite extends ADODB_pdo { var $pdoDriver = false; var $random='abs(random())'; - function _init($parentDriver) + protected function _init(ADODB_pdo $parentDriver) { + parent::_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 + // PDO SQLite driver does not support changing autocommit mode + $parentDriver->hasTransactions = false; $parentDriver->hasInsertID = true; } diff --git a/drivers/adodb-pdo_sqlsrv.inc.php b/drivers/adodb-pdo_sqlsrv.inc.php index de5fbba0..badc14f7 100644 --- a/drivers/adodb-pdo_sqlsrv.inc.php +++ b/drivers/adodb-pdo_sqlsrv.inc.php @@ -20,20 +20,22 @@ * @author Ned Andre */ -class ADODB_pdo_sqlsrv extends ADODB_pdo +class ADODB_pdo_sqlsrv extends ADODB_pdo_base { 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) + var $fmtDate = "'Y-m-d'"; + var $fmtTimeStamp = "'Y-m-d H:i:s'"; + + protected function _init(ADODB_pdo $parentDriver) { + parent::_init($parentDriver); + $parentDriver->hasTransactions = true; - $parentDriver->_bindInputArray = true; $parentDriver->hasInsertID = true; - $parentDriver->fmtTimeStamp = "'Y-m-d H:i:s'"; - $parentDriver->fmtDate = "'Y-m-d'"; } function setTransactionMode( $transaction_mode ) diff --git a/drivers/adodb-postgres64.inc.php b/drivers/adodb-postgres64.inc.php index cda1e962..b9678bd3 100644 --- a/drivers/adodb-postgres64.inc.php +++ b/drivers/adodb-postgres64.inc.php @@ -26,6 +26,10 @@ class ADODB_postgres64 extends ADOConnection{ var $databaseType = 'postgres64'; var $dataProvider = 'postgres'; var $hasInsertID = true; + /** @var PgSql\Connection|resource|false Identifier for the native database connection */ + var $_connectionID = false; + /** @var PgSql\Connection|resource|false */ + var $_queryID; /** @var PgSql\Connection|resource|false */ var $_resultid = false; var $concat_operator='||'; @@ -78,21 +82,21 @@ class ADODB_postgres64 extends ADOConnection{ // http://bugs.php.net/bug.php?id=25404 var $uniqueIisR = true; - var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database + var $_bindInputArray = false; // requires PostgreSQL 7.3+ and ability to modify database var $disableBlobs = false; // set to true to disable blob checking, resulting in 2-5% improvement in performance. /** @var int $_pnum Number of the last assigned query parameter {@see param()} */ var $_pnum = 0; var $version; - var $_nestedSQL = false; + var $_nestedSQL = true; // The last (fmtTimeStamp is not entirely correct: // PostgreSQL also has support for time zones, // and writes these time in this format: "2001-03-01 18:59:26+02". // There is no code for the "+02" time zone information, so I just left that out. // I'm not familiar enough with both ADODB as well as Postgres - // to know what the concequences are. The other values are correct (wheren't in 0.94) + // to know what the consequences are. The other values are correct (weren't in 0.94) // -- Freek Dijkstra /** @@ -167,7 +171,9 @@ class ADODB_postgres64 extends ADOConnection{ */ protected function _insertID($table = '', $column = '') { - if ($this->_resultid === false) return false; + if ($this->_resultid === false) { + return false; + } $oid = pg_last_oid($this->_resultid); // to really return the id, we need the table and column-name, else we can only return the oid != id return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid); @@ -175,7 +181,9 @@ class ADODB_postgres64 extends ADOConnection{ function _affectedrows() { - if ($this->_resultid === false) return false; + if ($this->_resultid === false) { + return false; + } return pg_affected_rows($this->_resultid); } @@ -190,13 +198,14 @@ class ADODB_postgres64 extends ADOConnection{ return pg_query($this->_connectionID, 'begin '.$this->_transmode); } - function RowLock($tables,$where,$col='1 as adodbignore') + function RowLock($table, $where, $col='1 as adodbignore') { - if (!$this->transCnt) $this->BeginTrans(); - return $this->GetOne("select $col from $tables where $where for update"); + if (!$this->transCnt) { + $this->BeginTrans(); + } + return $this->GetOne("select $col from $table where $where for update"); } - // returns true/false. function CommitTrans($ok=true) { if ($this->transOff) return true; @@ -206,7 +215,6 @@ class ADODB_postgres64 extends ADOConnection{ return pg_query($this->_connectionID, 'commit'); } - // returns true/false function RollbackTrans() { if ($this->transOff) return true; @@ -273,8 +281,6 @@ class ADODB_postgres64 extends ADOConnection{ } } - - // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; @@ -354,15 +360,24 @@ class ADODB_postgres64 extends ADOConnection{ - /* - * Load a Large Object from a file - * - the procedure stores the object id in the table and imports the object using - * postgres proprietary blob handling routines - * - * contributed by Mattia Rossi mattia@technologist.com - * modified for safe mode by juraj chlebec - */ - function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') + /** + * Update a BLOB from a file. + * + * The procedure stores the object id in the table and imports the object using + * postgres proprietary blob handling routines. + * + * Usage example: + * $conn->updateBlobFile('table', 'blob_col', '/path/to/file', 'id=1'); + * + * @param string $table + * @param string $column + * @param string $path Filename containing blob data + * @param mixed $where {@see updateBlob()} + * @param string $blobtype supports 'BLOB' and 'CLOB' + * + * @return bool success + */ + function updateBlobFile($table,$column,$path,$where,$blobtype='BLOB') { pg_query($this->_connectionID, 'begin'); @@ -378,24 +393,25 @@ class ADODB_postgres64 extends ADOConnection{ // $oid = pg_lo_import ($path); pg_query($this->_connectionID, 'commit'); $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); - $rez = !empty($rs); - return $rez; + return !empty($rs); } - /* - * Deletes/Unlinks a Blob from the database, otherwise it - * will be left behind - * - * Returns TRUE on success or FALSE on failure. - * - * contributed by Todd Rogers todd#windfox.net - */ - function BlobDelete( $blob ) + /** + * Deletes/Unlinks a Blob from the database, otherwise it will be left behind. + * + * contributed by Todd Rogers todd#windfox.net + * + * @param mixed $blob + * @return bool True on success, false on failure. + * + * @noinspection PhpUnused + */ + function BlobDelete($blob) { pg_query($this->_connectionID, 'begin'); $result = @pg_lo_unlink($this->_connectionID, $blob); pg_query($this->_connectionID, 'commit'); - return( $result ); + return $result; } /* @@ -407,19 +423,21 @@ class ADODB_postgres64 extends ADOConnection{ return is_numeric($oid); } - /* - * If an OID is detected, then we use pg_lo_* to open the oid file and read the - * real blob from the db using the oid supplied as a parameter. If you are storing - * blobs using bytea, we autodetect and process it so this function is not needed. - * - * contributed by Mattia Rossi mattia@technologist.com - * - * see http://www.postgresql.org/idocs/index.php?largeobjects.html - * - * Since adodb 4.54, this returns the blob, instead of sending it to stdout. Also - * added maxsize parameter, which defaults to $db->maxblobsize if not defined. - */ - function BlobDecode($blob,$maxsize=false,$hastrans=true) + /** + * If an OID is detected, then we use pg_lo_* to open the oid file and read the + * real blob from the db using the oid supplied as a parameter. If you are storing + * blobs using bytea, we autodetect and process it so this function is not needed. + * + * Contributed by Mattia Rossi mattia@technologist.com + * + * @link https://www.postgresql.org/docs/current/largeobjects.html + * + * @param mixed $blob + * @param int|false $maxsize Defaults to $db->maxblobsize if false + * @param bool $hastrans + * @return string|false The blob + */ + function BlobDecode($blob, $maxsize=false, $hastrans=true) { if (!$this->GuessOID($blob)) return $blob; @@ -439,7 +457,7 @@ class ADODB_postgres64 extends ADOConnection{ /** * Encode binary value prior to DB storage. * - * See https://www.postgresql.org/docs/current/static/datatype-binary.html + * @link 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 @@ -500,7 +518,6 @@ class ADODB_postgres64 extends ADOConnection{ global $ADODB_FETCH_MODE; $schema = false; - $false = false; $this->_findschema($table,$schema); if ($normalize) $table = strtolower($table); @@ -514,7 +531,7 @@ class ADODB_postgres64 extends ADOConnection{ $ADODB_FETCH_MODE = $save; if ($rs === false) { - return $false; + return false; } if (!empty($this->metaKeySQL)) { // If we want the primary keys, we have to issue a separate query @@ -532,6 +549,8 @@ class ADODB_postgres64 extends ADOConnection{ $rskey->Close(); unset($rskey); + } else { + $keys = []; } $rsdefa = array(); @@ -584,28 +603,29 @@ class ADODB_postgres64 extends ADOConnection{ //Freek $fld->not_null = $rs->fields[4] == 't'; - // Freek if (is_array($keys)) { foreach($keys as $key) { - if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') + if ($fld->name == $key['column_name'] && $key['primary_key'] == 't') { $fld->primary_key = true; - if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') + } + if ($fld->name == $key['column_name'] && $key['unique_key'] == 't') { $fld->unique = true; // What name is more compatible? + } } } - if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; - else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) { + $retarr[] = $fld; + } + else { + $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; + } $rs->MoveNext(); } $rs->Close(); - if (empty($retarr)) - return $false; - else - return $retarr; - + return $retarr ?: false; } function param($name, $type='C') @@ -649,7 +669,7 @@ class ADODB_postgres64 extends ADOConnection{ WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))'; } - if ($primary == FALSE) { + if (!$primary) { $sql .= ' AND i.indisprimary=false;'; } @@ -666,8 +686,7 @@ class ADODB_postgres64 extends ADOConnection{ $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { - $false = false; - return $false; + return false; } // Get column names indexed by attnum so we can lookup the index key @@ -758,22 +777,6 @@ class ADODB_postgres64 extends ADOConnection{ if ($this->_connectionID === false) return false; $this->Execute("set datestyle='ISO'"); - $info = $this->ServerInfo(false); - - if (version_compare($info['version'], '7.1', '>=')) { - $this->_nestedSQL = true; - } - - # PostgreSQL 9.0 changed the default output for bytea from 'escape' to 'hex' - # 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 (version_compare($info['version'], '9.0', '>=') - && version_compare($info['client'], '9.2', '<') - ) { - $this->Execute('set bytea_output=escape'); - } - return true; } @@ -920,17 +923,17 @@ class ADODB_postgres64 extends ADOConnection{ } - /* - * Maximum size of C field - */ + /** + * @return int Maximum size of C field + */ function CharMax() { return 1000000000; // should be 1 Gb? } - /* - * Maximum size of X field - */ + /** + * @return int Maximum size of X field + */ function TextMax() { return 1000000000; // should be 1 Gb? @@ -950,23 +953,21 @@ class ADORecordSet_postgres64 extends ADORecordSet{ function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) - { - case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; - case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; + parent::__construct($queryID, $mode); - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH: - default: $this->fetchMode = PGSQL_BOTH; break; + switch ($this->adodbFetchMode) { + case ADODB_FETCH_NUM: + $this->fetchMode = PGSQL_NUM; + break; + case ADODB_FETCH_ASSOC: + $this->fetchMode = PGSQL_ASSOC; + break; + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH: + default: + $this->fetchMode = PGSQL_BOTH; + break; } - $this->adodbFetchMode = $mode; - - // Parent's constructor - parent::__construct($queryID); } function GetRowAssoc($upper = ADODB_ASSOC_CASE) @@ -974,8 +975,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ if ($this->fetchMode == PGSQL_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) { return $this->fields; } - $row = ADORecordSet::GetRowAssoc($upper); - return $row; + return ADORecordSet::GetRowAssoc($upper); } function _initRS() @@ -987,7 +987,6 @@ class ADORecordSet_postgres64 extends ADORecordSet{ // cache types for blob decode check // apparently pg_field_type actually performs an sql query on the database to get the type. - if (empty($this->connection->noBlobs)) for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { if (pg_field_type($qid,$i) == 'bytea') { $this->_blobArr[$i] = pg_field_name($qid,$i); @@ -1054,7 +1053,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ } } if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) { - foreach($this->_blobArr as $k => $v) { + foreach($this->_blobArr as $v) { $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]); } } @@ -1095,7 +1094,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ return pg_free_result($this->_queryID); } - function MetaType($t,$len=-1,$fieldobj=false) + function MetaType($t,$len=-1, $fieldObj=false) { if (is_object($t)) { $fieldobj = $t; @@ -1105,71 +1104,81 @@ class ADORecordSet_postgres64 extends ADORecordSet{ $t = strtoupper($t); - if (array_key_exists($t,$this->connection->customActualTypes)) - return $this->connection->customActualTypes[$t]; + if (array_key_exists($t, $this->connection->customActualTypes)) { + return $this->connection->customActualTypes[$t]; + } switch ($t) { - case 'MONEY': // stupid, postgres expects money to be a string - case 'INTERVAL': - case 'CHAR': - case 'CHARACTER': - case 'VARCHAR': - case 'NAME': - case 'BPCHAR': - case '_VARCHAR': - case 'CIDR': - case 'INET': - case 'MACADDR': - case 'UUID': - if ($len <= $this->blobSize) return 'C'; - - case 'TEXT': - return 'X'; + case 'MONEY': // stupid, postgres expects money to be a string + case 'INTERVAL': + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + case '_VARCHAR': + case 'CIDR': + case 'INET': + case 'MACADDR': + /** @noinspection PhpMissingBreakStatementInspection */ + case 'UUID': + if ($len <= $this->blobSize) { + return 'C'; + } + // Fall-through - case 'IMAGE': // user defined type - case 'BLOB': // user defined type - case 'BIT': // This is a bit string, not a single bit, so don't return 'L' - case 'VARBIT': - case 'BYTEA': - return 'B'; + case 'TEXT': + return 'X'; - case 'BOOL': - case 'BOOLEAN': - return 'L'; + case 'IMAGE': // user defined type + case 'BLOB': // user defined type + case 'BIT': // This is a bit string, not a single bit, so don't return 'L' + case 'VARBIT': + case 'BYTEA': + return 'B'; - case 'DATE': - return 'D'; + case 'BOOL': + case 'BOOLEAN': + return 'L'; + case 'DATE': + return 'D'; - case 'TIMESTAMP WITHOUT TIME ZONE': - case 'TIME': - case 'DATETIME': - case 'TIMESTAMP': - case 'TIMESTAMPTZ': - return 'T'; + case 'TIMESTAMP WITHOUT TIME ZONE': + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + case 'TIMESTAMPTZ': + return 'T'; - case 'SMALLINT': - case 'BIGINT': - case 'INTEGER': - case 'INT8': - case 'INT4': - case 'INT2': - if (isset($fieldobj) && - empty($fieldobj->primary_key) && (!$this->connection->uniqueIisR || empty($fieldobj->unique))) return 'I'; + case 'SMALLINT': + case 'BIGINT': + case 'INTEGER': + case 'INT8': + case 'INT4': + /** @noinspection PhpMissingBreakStatementInspection */ + case 'INT2': + if (isset($fieldobj) + && empty($fieldobj->primary_key) + && (!$this->connection->uniqueIisR || empty($fieldobj->unique)) + ) { + return 'I'; + } + // Fall-through - case 'OID': - case 'SERIAL': - return 'R'; + case 'OID': + case 'SERIAL': + return 'R'; - case 'NUMERIC': - case 'DECIMAL': - case 'FLOAT4': - case 'FLOAT8': - return 'N'; + case 'NUMERIC': + case 'DECIMAL': + case 'FLOAT4': + case 'FLOAT8': + return 'N'; - default: - return ADODB_DEFAULT_METATYPE; - } + default: + return ADODB_DEFAULT_METATYPE; + } } } diff --git a/drivers/adodb-sqlite.inc.php b/drivers/adodb-sqlite.inc.php index d41829c0..e9fab6dc 100644 --- a/drivers/adodb-sqlite.inc.php +++ b/drivers/adodb-sqlite.inc.php @@ -36,8 +36,8 @@ class ADODB_sqlite extends ADOConnection { var $hasInsertID = true; /// supports autoincrement ID? var $hasAffectedRows = true; /// supports affected rows for update/delete? var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; - var $sysDate = "adodb_date('Y-m-d')"; - var $sysTimeStamp = "adodb_date('Y-m-d H:i:s')"; + var $sysDate = "date('Y-m-d')"; + var $sysTimeStamp = "date('Y-m-d H:i:s')"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; function ServerInfo() @@ -164,14 +164,24 @@ class ADODB_sqlite extends ADOConnection { function SQLDate($fmt, $col=false) { $fmt = $this->qstr($fmt); - return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)"; + return ($col) ? "adodb_date($fmt,$col)" : "adodb_date($fmt)"; } function _createFunctions() { - @sqlite_create_function($this->_connectionID, 'adodb_date', 'adodb_date', 1); - @sqlite_create_function($this->_connectionID, 'adodb_date2', 'adodb_date2', 2); + // Register date conversion function for SQLDate() method + // Replaces the legacy adodb_date() functions removed in 5.23.0 + @sqlite_create_function($this->_connectionID, 'adodb_date', + function (string $fmt, $date = null) : string { + if ($date === null || $date === false) { + return date($fmt); + } + + // If it's an int then assume a Unix timestamp, otherwise convert it + return date($fmt, is_int($date) ? $date : strtotime($date)); + } + ); } @@ -425,14 +435,11 @@ class ADORecordset_sqlite extends ADORecordSet { var $databaseType = "sqlite"; var $bind = false; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { + parent::__construct($queryID, $mode); - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch($mode) { + switch ($this->adodbFetchMode) { case ADODB_FETCH_NUM: $this->fetchMode = SQLITE_NUM; break; @@ -443,9 +450,6 @@ class ADORecordset_sqlite extends ADORecordSet { $this->fetchMode = SQLITE_BOTH; break; } - $this->adodbFetchMode = $mode; - - $this->_queryID = $queryID; $this->_inited = true; $this->fields = array(); @@ -462,7 +466,6 @@ class ADORecordset_sqlite extends ADORecordSet { return $this->_queryID; } - function FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; diff --git a/drivers/adodb-sqlite3.inc.php b/drivers/adodb-sqlite3.inc.php index 026e0392..696e2990 100644 --- a/drivers/adodb-sqlite3.inc.php +++ b/drivers/adodb-sqlite3.inc.php @@ -42,11 +42,22 @@ class ADODB_sqlite3 extends ADOConnection { var $sysDate = "DATE('now','localtime')"; var $sysTimeStamp = "DATETIME('now','localtime')"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; + var $_genSeqSQL = "create table %s (id integer)"; + var $_dropSeqSQL = 'drop table %s'; - /** @var SQLite3 */ + /** + * The SQLite3 connection object + * + * @var SQLite3 + */ var $_connectionID; - function ServerInfo() + /** + * Returns an array with the server information + * + * @return array + */ + function serverInfo() { $version = SQLite3::version(); $arr['version'] = $version['versionString']; @@ -54,7 +65,24 @@ class ADODB_sqlite3 extends ADOConnection { return $arr; } - function BeginTrans() + /** + * Return string with a database specific IFNULL statement + * + * @param string $field Field name to check for null + * @param string $ifNull Value to return if $field is null + * + * @return string + */ + function ifNull( $field, $ifNull ) { + return " IFNULL($field, $ifNull) "; // if SQLite 3.X + } + + /** + * Begin a transaction + * + * @return bool + */ + function beginTrans() { if ($this->transOff) { return true; @@ -64,7 +92,14 @@ class ADODB_sqlite3 extends ADOConnection { return true; } - function CommitTrans($ok=true) + /** + * Commit a transaction + * + * @param bool $ok If false, will rollback the transaction + * + * @return bool + */ + function commitTrans($ok=true) { if ($this->transOff) { return true; @@ -79,7 +114,12 @@ class ADODB_sqlite3 extends ADOConnection { return !empty($ret); } - function RollbackTrans() + /** + * Rollback a transaction + * + * @return bool + */ + function rollbackTrans() { if ($this->transOff) { return true; @@ -91,11 +131,19 @@ class ADODB_sqlite3 extends ADOConnection { return !empty($ret); } + /** + * Returns the ADOdb metatype for a given SQLite type + * + * @param string|ADOFieldObject $t The type to convert + * @param int $len The length of the field (not used) + * @param bool $fieldobj If true, $t is an ADOFieldObject + * + * @return string The ADOdb metatype + */ function metaType($t,$len=-1,$fieldobj=false) { - if (is_object($t)) - { + if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; } @@ -160,8 +208,16 @@ class ADODB_sqlite3 extends ADOConnection { return $subclass[$subt]; } - // mark newnham - function MetaColumns($table, $normalize=true) + + /** + * Returns the metadata for a table + * + * @param string $table The table name + * @param bool $normalize If true, will return the field names in uppercase + * + * @return array|false An array of ADOFieldObject objects or false on failure + */ + function metaColumns($table, $normalize=true) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; @@ -183,6 +239,9 @@ class ADODB_sqlite3 extends ADOConnection { $arr = array(); while ($r = $rs->FetchRow()) { + // Metacolumns returns column names in lowercase + $r = array_change_key_case($r, CASE_LOWER); + $type = explode('(', $r['type']); $size = ''; if (sizeof($type) == 2) { @@ -209,6 +268,16 @@ class ADODB_sqlite3 extends ADOConnection { return $arr; } + /** + * Returns the foreign keys for a table + * + * @param string $table The table name + * @param string $owner The owner of the table (not used) + * @param bool $upper If true, will return uppercase table names + * @param bool $associative If true, will return an associative array + * + * @return array An array of foreign keys or false on failure + */ public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) { global $ADODB_FETCH_MODE; @@ -249,88 +318,170 @@ class ADODB_sqlite3 extends ADOConnection { return $fkeyList; } + /** + * Initialize the driver + * + * @param ADOConnection $parentDriver The parent connection object + * + * @return void + */ function _init($parentDriver) { $parentDriver->hasTransactions = false; $parentDriver->hasInsertID = true; } + /** + * Returns the last inserted ID + * + * @param string $table The table name (not used) + * @param string $column The column name (not used) + * + * @return int The last inserted ID + */ protected function _insertID($table = '', $column = '') { return $this->_connectionID->lastInsertRowID(); } + /** + * Returns the number of affected rows by the last query + * + * @return int The number of affected rows + */ function _affectedrows() { return $this->_connectionID->changes(); } + /** + * Sets the last error message and code + * + * This is called after a failed query to set the error message and code + * + * @return void + */ protected function lastError() { $this->_errorMsg = $this->_connectionID->lastErrorMsg(); $this->_errorCode = $this->_connectionID->lastErrorCode(); } - function ErrorMsg() - { + /** + * Returns the last error message + * + * @return string The last error message + */ + function errorMsg() + { return $this->_errorMsg; } - function ErrorNo() + /** + * Returns the last error code + * + * @return int The last error code + */ + function errorNo() { return $this->_errorCode; } - function SQLDate($fmt, $col=false) + /** + * Returns a formatted date string for SQLite + * + * This function formats the date according to the SQLite strftime function + * and ensures proper casing for certain fields. + * + * @param string $fmt The format string + * @param bool $col If true, will use the column name in the format + * + * @return string The formatted date string + */ + 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); + // 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); - $this->_connectionID->createFunction('adodb_date2', 'adodb_date2', 2); + return $col ? "adodb_date($fmt,$col)" : "adodb_date($fmt)"; } - /** @noinspection PhpUnusedParameterInspection */ + /** + * Connects to the SQLite database + * + * @param string $argHostname The hostname or database file path + * @param string $argUsername The username (not used) + * @param string $argPassword The password (not used) + * @param string $argDatabasename The database name (not used) + * + * @noinspection PhpUnusedParameterInspection + * + * @return bool True on success, false on failure + */ function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (empty($argHostname) && $argDatabasename) { $argHostname = $argDatabasename; } $this->_connectionID = new SQLite3($argHostname); - $this->_createFunctions(); + + // Register date conversion function for SQLDate() method + // Replaces the legacy adodb_date() functions removed in 5.23.0 + $this->_connectionID->createFunction('adodb_date', + function (string $fmt, $date = null) : string { + if ($date === null || $date === false) { + return date($fmt); + } + + // If it's an int then assume a Unix timestamp, otherwise convert it + return date($fmt, is_int($date) ? $date : strtotime($date)); + } + ); return true; } + /** + * Connects to the SQLite database using a persistent connection + * + * @param string $argHostname The hostname or database file path + * @param string $argUsername The username (not used) + * @param string $argPassword The password (not used) + * @param string $argDatabasename The database name (not used) + * + * @noinspection PhpUnusedParameterInspection + * + * @return bool True on success, false on failure + */ function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { // There's no permanent connect in SQLite3 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); } + /** + * Executes a query on the SQLite database + * + * @param string $sql The SQL query to execute + * @param mixed $inputarr An array of input parameters (not used) + * + * @return SQLite3Result|bool The result set or true on success, false on failure + */ function _query($sql,$inputarr=false) { $rez = $this->_connectionID->query($sql); if ($rez === false) { $this->lastError(); - } - // If no data was returned, we don't need to create a real recordset - elseif ($rez->numColumns() == 0) { + } elseif ($rez->numColumns() == 0) { + // If no data was returned, we don't need to create a real recordset $rez->finalize(); $rez = true; } @@ -338,7 +489,21 @@ class ADODB_sqlite3 extends ADOConnection { return $rez; } - function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) + /** + * Executes a query with a limit and offset + * + * This function modifies the SQL query to include a LIMIT and OFFSET clause + * based on the provided parameters. + * + * @param string $sql The SQL query to execute + * @param int $nrows The number of rows to return (default -1 for no limit) + * @param int $offset The offset to start returning rows from (default -1 for no offset) + * @param mixed $inputarr An array of input parameters (not used) + * @param int $secs2cache Number of seconds to cache the result (default 0 for no caching) + * + * @return SQLite3Result|bool The result set or true on success, false on failure + */ + function selectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { $nrows = (int) $nrows; $offset = (int) $offset; @@ -353,15 +518,19 @@ class ADODB_sqlite3 extends ADOConnection { 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. + /** + * Generates a unique ID using a sequence table + * + * This function uses a sequence table to generate unique IDs. If the sequence + * table does not exist, it will create it. The function will return false if + * it is unable to generate a unique ID after a specified number of attempts. + * + * @param string $seqname The name of the sequence table (default 'adodbseq') + * @param int $startID The starting ID value (default 1) + * + * @return int|false The generated unique ID or false on failure */ - var $_genSeqSQL = "create table %s (id integer)"; - - function GenID($seqname='adodbseq', $startID=1) + function genID($seqname='adodbseq', $startID=1) { // if you have to modify the parameter below, your database is overloaded, // or you need to implement generation of id's yourself! @@ -392,6 +561,18 @@ class ADODB_sqlite3 extends ADOConnection { return false; } + /** + * Creates a sequence table + * + * This function creates a sequence table with the specified name and starts + * the ID at the specified value. If the sequence table already exists, it will + * return false. + * + * @param string $seqname The name of the sequence table (default 'adodbseq') + * @param int $startID The starting ID value (default 1) + * + * @return bool True on success, false on failure + */ function createSequence($seqname='adodbseq', $startID=1) { if (empty($this->_genSeqSQL)) { @@ -405,8 +586,17 @@ class ADODB_sqlite3 extends ADOConnection { return $this->Execute("insert into $seqname values($startID)"); } - var $_dropSeqSQL = 'drop table %s'; - function DropSequence($seqname = 'adodbseq') + /** + * Drops a sequence table + * + * This function drops the specified sequence table. If the sequence table does + * not exist or if the drop SQL is not set, it will return false. + * + * @param string $seqname The name of the sequence table (default 'adodbseq') + * + * @return bool True on success, false on failure + */ + function dropSequence($seqname = 'adodbseq') { if (empty($this->_dropSeqSQL)) { return false; @@ -414,12 +604,30 @@ class ADODB_sqlite3 extends ADOConnection { return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); } - // returns true or false + /** + * Closes the SQLite connection + * + * This function closes the SQLite connection and returns true on success. + * + * @return bool True on success, false on failure + */ function _close() { return $this->_connectionID->close(); } + /** + * Returns the indexes for a table + * + * This function retrieves the indexes for a given table from the SQLite master table. + * It can also return the primary key index if requested. + * + * @param string $table The table name + * @param bool $primary If true, will include the primary key index + * @param bool $owner Not used, included for compatibility + * + * @return array|false An array of indexes or false on failure + */ function metaIndexes($table, $primary = false, $owner = false) { // save old fetch mode @@ -559,8 +767,8 @@ class ADODB_sqlite3 extends ADOConnection { * so $where can either be an array (array params) or a string that we will * do our best to unpack and turn into a prepared statement. * - * @param string $table - * @param string $column + * @param string $table The table name + * @param string $column The column name to update * @param string $val Blob value to set * @param mixed $where An array of parameters (key => value pairs), * or a string (where clause). @@ -636,8 +844,8 @@ class ADODB_sqlite3 extends ADOConnection { /** * SQLite update for blob from a file * - * @param string $table - * @param string $column + * @param string $table The table name + * @param string $column The column name to update * @param string $path Filename containing blob data * @param mixed $where {@see updateBlob()} * @param string $blobtype ignored @@ -665,22 +873,37 @@ class ADODB_sqlite3 extends ADOConnection { Class Name: Recordset --------------------------------------------------------------------------------------*/ +/** + * Class ADORecordset_sqlite3 + * + * This class extends ADORecordSet to provide SQLite3 specific functionality. + * It handles fetching records, field metadata, and other recordset operations. + */ class ADORecordset_sqlite3 extends ADORecordSet { var $databaseType = "sqlite3"; var $bind = false; - /** @var SQLite3Result */ + /** + * The SQLite3Result object + * + * @var SQLite3Result + */ var $_queryID; - /** @noinspection PhpMissingParentConstructorInspection */ - function __construct($queryID,$mode=false) + + /** + * Constructor for the ADORecordset_sqlite3 class + * + * @param SQLite3Result $queryID The SQLite3Result object + * @param bool $mode The fetch mode (default false) + * + * @noinspection PhpMissingParentConstructorInspection + */ + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch($mode) { + parent::__construct($queryID, $mode); + switch($this->adodbFetchMode) { case ADODB_FETCH_NUM: $this->fetchMode = SQLITE3_NUM; break; @@ -691,9 +914,6 @@ class ADORecordset_sqlite3 extends ADORecordSet { $this->fetchMode = SQLITE3_BOTH; break; } - $this->adodbFetchMode = $mode; - - $this->_queryID = $queryID; $this->_inited = true; $this->fields = array(); @@ -710,8 +930,17 @@ class ADORecordset_sqlite3 extends ADORecordSet { return $this->_queryID; } - - function FetchField($fieldOffset = -1) + /** + * Returns the field object for a given field offset + * + * This function retrieves the field object for a specific field offset. + * It creates a new ADOFieldObject and sets its name and type. + * + * @param int $fieldOffset The offset of the field (default -1 for the first field) + * + * @return ADOFieldObject The field object + */ + function fetchField($fieldOffset = -1) { $fld = new ADOFieldObject; $fld->name = $this->_queryID->columnName($fieldOffset); @@ -720,13 +949,32 @@ class ADORecordset_sqlite3 extends ADORecordSet { return $fld; } + /** + * Initializes the recordset by setting the number of fields + * + * This function is called after the query has been executed to set the + * number of fields in the recordset. + * + * @return void + */ function _initrs() { $this->_numOfFields = $this->_queryID->numColumns(); } - function Fields($colname) + /** + * Returns the value of a field by its name + * + * This function retrieves the value of a field by its name. If the fetch mode + * is not numeric, it will return the value directly. If it is numeric, it will + * use a binding array to map the column names to their respective indices. + * + * @param string $colname The name of the column + * + * @return mixed The value of the field + */ + function fields($colname) { if ($this->fetchMode != SQLITE3_NUM) { return $this->fields[$colname]; @@ -742,6 +990,16 @@ class ADORecordset_sqlite3 extends ADORecordSet { return $this->fields[$this->bind[strtoupper($colname)]]; } + /** + * Returns the number of rows in the recordset + * + * This function returns the number of rows in the recordset. If the recordset + * is empty, it will return 0. + * + * @param array $row ignored for SQLite3 + * + * @return int The number of rows in the recordset + */ function _seek($row) { // sqlite3 does not implement seek @@ -751,12 +1009,31 @@ class ADORecordset_sqlite3 extends ADORecordSet { return false; } + /** + * Uses the SQLite3 fetchArray method to retrieve the next row. + * + * @param bool $ignore_fields discarded for SQLite3. + * + * @return bool + */ function _fetch($ignore_fields=false) { $this->fields = $this->_queryID->fetchArray($this->fetchMode); + if (!empty($this->fields) && ADODB_ASSOC_CASE != ADODB_ASSOC_CASE_NATIVE) { + $this->fields = array_change_key_case($this->fields, ADODB_ASSOC_CASE); + } + return !empty($this->fields); } + /** + * Closes the recordset + * + * This function is called to close the recordset. It does not perform any + * specific actions for SQLite3, as the connection is managed by the ADOConnection class. + * + * @return void + */ function _close() { } diff --git a/drivers/adodb-sybase.inc.php b/drivers/adodb-sybase.inc.php index 699025f2..6c6a2101 100644 --- a/drivers/adodb-sybase.inc.php +++ b/drivers/adodb-sybase.inc.php @@ -315,15 +315,11 @@ class ADORecordset_sybase extends ADORecordSet { // _mths works only in non-localised system var $_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); - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC; - else $this->fetchMode = $mode; - parent::__construct($id); + parent::__construct($queryID, $mode); + + $this->fetchMode = $this->adodbFetchMode ?: ADODB_FETCH_ASSOC; } /* Returns: an object containing field information. @@ -412,7 +408,7 @@ class ADORecordSet_array_sybase extends ADORecordSet_array { $themth = $ADODB_sybase_mths[$themth]; if ($themth <= 0) return false; // h-m-s-MM-DD-YY - return adodb_mktime(0,0,0,$themth,$rr[2],$rr[3]); + return mktime(0,0,0,$themth,$rr[2],$rr[3]); } static function UnixTimeStamp($v) @@ -439,6 +435,6 @@ class ADORecordSet_array_sybase extends ADORecordSet_array { break; } // h-m-s-MM-DD-YY - return adodb_mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]); + return mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]); } } |
