summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Regad <dregad@mantisbt.org>2021-08-22 12:35:05 +0200
committerDamien Regad <dregad@mantisbt.org>2021-08-22 12:36:57 +0200
commitc823ab6795c1bede93e8c2649129dcf5f9f4660f (patch)
tree141a0cc2d3c15425645beaa9daa5b353642bbd30
parent4ea987402d2bee61986149ab4aac66c598d220f6 (diff)
parentd03e8bc5571ab6bae2fb3d04f6c8e897d6b7893d (diff)
downloadadodb-c823ab6795c1bede93e8c2649129dcf5f9f4660f.tar.gz
adodb-c823ab6795c1bede93e8c2649129dcf5f9f4660f.tar.bz2
adodb-c823ab6795c1bede93e8c2649129dcf5f9f4660f.zip
New release of Firebird Driver
This new release of the driver for Firebird is designed for use with the latest PHP7 drivers [[1]]. Fixes #710, PR #720 [1]: https://github.com/FirebirdSQL/php-firebird
-rw-r--r--adodb-datadict.inc.php24
-rw-r--r--datadict/datadict-firebird.inc.php242
-rw-r--r--drivers/adodb-firebird.inc.php1150
3 files changed, 928 insertions, 488 deletions
diff --git a/adodb-datadict.inc.php b/adodb-datadict.inc.php
index bfacb5ff..c73d5f2a 100644
--- a/adodb-datadict.inc.php
+++ b/adodb-datadict.inc.php
@@ -353,7 +353,7 @@ class ADODB_DataDict {
function nameQuote($name = NULL,$allowBrackets=false)
{
if (!is_string($name)) {
- return FALSE;
+ return false;
}
$name = trim($name);
@@ -428,14 +428,14 @@ class ADODB_DataDict {
function actualType($meta)
{
$meta = strtoupper($meta);
-
+
/*
* Add support for custom meta types. We do this
* first, that allows us to override existing types
*/
if (isset($this->connection->customMetaTypes[$meta]))
return $this->connection->customMetaTypes[$meta]['actual'];
-
+
return $meta;
}
@@ -704,23 +704,23 @@ class ADODB_DataDict {
case '0':
case 'NAME': $fname = $v; break;
case '1':
- case 'TYPE':
-
- $ty = $v;
- $ftype = $this->actualType(strtoupper($v));
+ case 'TYPE':
+
+ $ty = $v;
+ $ftype = $this->actualType(strtoupper($v));
break;
case 'SIZE':
- $dotat = strpos($v,'.');
- if ($dotat === false)
+ $dotat = strpos($v,'.');
+ if ($dotat === false)
$dotat = strpos($v,',');
- if ($dotat === false)
+ if ($dotat === false)
$fsize = $v;
else {
-
+
$fsize = substr($v,0,$dotat);
$fprec = substr($v,$dotat+1);
-
+
}
break;
case 'UNSIGNED': $funsigned = true; break;
diff --git a/datadict/datadict-firebird.inc.php b/datadict/datadict-firebird.inc.php
index ba80699d..79d0a8f5 100644
--- a/datadict/datadict-firebird.inc.php
+++ b/datadict/datadict-firebird.inc.php
@@ -22,8 +22,8 @@
// security - hide paths
if (!defined('ADODB_DIR')) die();
-class ADODB2_firebird extends ADODB_DataDict {
-
+class ADODB2_firebird extends ADODB_DataDict
+{
var $databaseType = 'firebird';
var $seqField = false;
var $seqPrefix = 's_';
@@ -32,79 +32,92 @@ class ADODB2_firebird extends ADODB_DataDict {
var $alterCol = ' ALTER';
var $dropCol = ' DROP';
- function ActualType($meta)
+ function actualType($meta)
{
-
+
$meta = strtoupper($meta);
-
- /*
- * Add support for custom meta types. We do this
- * first, that allows us to override existing types
- */
- if (isset($this->connection->customMetaTypes[$meta]))
+
+ // Add support for custom meta types.
+ // We do this first, that allows us to override existing types
+ if (isset($this->connection->customMetaTypes[$meta])) {
return $this->connection->customMetaTypes[$meta]['actual'];
-
+ }
+
switch($meta) {
- case 'C': return 'VARCHAR';
- case 'XL':
- case 'X': return 'BLOB SUB_TYPE TEXT';
+ case 'C':
+ return 'VARCHAR';
+ case 'XL':
+ return 'BLOB SUB_TYPE BINARY';
+ case 'X':
+ return 'BLOB SUB_TYPE TEXT';
- case 'C2': return 'VARCHAR(32765)'; // up to 32K
- case 'X2': return 'VARCHAR(4096)';
+ case 'C2':
+ return 'VARCHAR(32765)'; // up to 32K
+ case 'X2':
+ return 'VARCHAR(4096)';
- case 'V': return 'CHAR';
- case 'C1': return 'CHAR(1)';
+ case 'V':
+ return 'CHAR';
+ case 'C1':
+ return 'CHAR(1)';
- case 'B': return 'BLOB';
+ case 'B':
+ return 'BLOB';
- case 'D': return 'DATE';
- case 'TS':
- case 'T': return 'TIMESTAMP';
+ case 'D':
+ return 'DATE';
+ case 'TS':
+ case 'T':
+ return 'TIMESTAMP';
- case 'L': return 'SMALLINT';
- case 'I': return 'INTEGER';
- case 'I1': return 'SMALLINT';
- case 'I2': return 'SMALLINT';
- case 'I4': return 'INTEGER';
- case 'I8': return 'BIGINT';
+ case 'L':
+ case 'I1':
+ case 'I2':
+ return 'SMALLINT';
+ case 'I':
+ case 'I4':
+ return 'INTEGER';
+ case 'I8':
+ return 'BIGINT';
- case 'F': return 'DOUBLE PRECISION';
- case 'N': return 'DECIMAL';
- default:
- return $meta;
+ case 'F':
+ return 'DOUBLE PRECISION';
+ case 'N':
+ return 'DECIMAL';
+ default:
+ return $meta;
}
}
- function NameQuote($name = NULL,$allowBrackets=false)
+ function nameQuote($name = null, $allowBrackets = false)
{
if (!is_string($name)) {
- return FALSE;
+ return false;
}
$name = trim($name);
- if ( !is_object($this->connection) ) {
+ if (!is_object($this->connection)) {
return $name;
}
$quote = $this->connection->nameQuote;
// if name is of the form `name`, quote it
- if ( preg_match('/^`(.+)`$/', $name, $matches) ) {
+ if (preg_match('/^`(.+)`$/', $name, $matches)) {
return $quote . $matches[1] . $quote;
}
// if name contains special characters, quote it
- if ( !preg_match('/^[' . $this->nameRegex . ']+$/', $name) ) {
+ if (!preg_match('/^[' . $this->nameRegex . ']+$/', $name)) {
return $quote . $name . $quote;
}
return $quote . $name . $quote;
}
- function CreateDatabase($dbname, $options=false)
+ function createDatabase($dbname, $options = false)
{
- $options = $this->_Options($options);
$sql = array();
$sql[] = "DECLARE EXTERNAL FUNCTION LOWER CSTRING(80) RETURNS CSTRING(80) FREE_IT ENTRY_POINT 'IB_UDF_lower' MODULE_NAME 'ib_udf'";
@@ -112,50 +125,62 @@ class ADODB2_firebird extends ADODB_DataDict {
return $sql;
}
- function _DropAutoIncrement($t)
+ function _dropAutoIncrement($tabname)
{
- if (strpos($t,'.') !== false) {
- $tarr = explode('.',$t);
- return 'DROP GENERATOR '.$tarr[0].'."s_'.$tarr[1].'"';
+ if (strpos($tabname, '.') !== false) {
+ $tarr = explode('.', $tabname);
+ return 'DROP SEQUENCE ' . $tarr[0] . '."s_' . $tarr[1] . '"';
}
- return 'DROP GENERATOR s_'.$t;
+ return 'DROP SEQUENCE s_' . $tabname;
}
- function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+ function _createSuffix($fname, &$ftype, $fnotnull, $fdefault, $fautoinc, $fconstraint, $funsigned)
{
$suffix = '';
- if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
- if ($fnotnull) $suffix .= ' NOT NULL';
- if ($fautoinc) $this->seqField = $fname;
+ if (strlen($fdefault)) {
+ $suffix .= " DEFAULT $fdefault";
+ }
+ if ($fnotnull) {
+ $suffix .= ' NOT NULL';
+ }
+ if ($fautoinc) {
+ $this->seqField = $fname;
+ }
$fconstraint = preg_replace("/``/", "\"", $fconstraint);
- if ($fconstraint) $suffix .= ' '.$fconstraint;
+ if ($fconstraint) {
+ $suffix .= ' ' . $fconstraint;
+ }
return $suffix;
}
/**
- Generate the SQL to create table. Returns an array of sql strings.
- */
- function CreateTableSQL($tabname, $flds, $tableoptions=array())
+ * Generate the SQL to create table. Returns an array of sql strings.
+ */
+ function createTableSQL($tabname, $flds, $tableoptions = array())
{
- list($lines,$pkey,$idxs) = $this->_GenFields($flds, true);
+ list($lines, $pkey, $idxs) = $this->_GenFields($flds, true);
// genfields can return FALSE at times
- if ($lines == null) $lines = array();
+ if ($lines == null) {
+ $lines = array();
+ }
$taboptions = $this->_Options($tableoptions);
- $tabname = $this->TableName ($tabname);
- $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions);
+ $tabname = $this->TableName($tabname);
+ $sql = $this->_TableSQL($tabname, $lines, $pkey, $taboptions);
- if ($this->autoIncrement && !isset($taboptions['DROP']))
- { $tsql = $this->_Triggers($tabname,$taboptions);
- foreach($tsql as $s) $sql[] = $s;
+ if ($this->autoIncrement && !isset($taboptions['DROP'])) {
+ $tsql = $this->_Triggers($tabname, $taboptions);
+ foreach ($tsql as $s) {
+ $sql[] = $s;
+ }
}
if (is_array($idxs)) {
- foreach($idxs as $idx => $idxdef) {
- $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
+ foreach ($idxs as $idx => $idxdef) {
+ $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
$sql = array_merge($sql, $sql_idxs);
}
}
@@ -164,44 +189,47 @@ class ADODB2_firebird extends ADODB_DataDict {
}
-/*
-CREATE or replace TRIGGER jaddress_insert
-before insert on jaddress
-for each row
-begin
-IF ( NEW."seqField" IS NULL OR NEW."seqField" = 0 ) THEN
- NEW."seqField" = GEN_ID("GEN_tabname", 1);
-end;
-*/
- function _Triggers($tabname,$tableoptions)
+ /*
+ CREATE or replace TRIGGER jaddress_insert
+ before insert on jaddress
+ for each row
+ begin
+ IF ( NEW."seqField" IS NULL OR NEW."seqField" = 0 ) THEN
+ NEW."seqField" = GEN_ID("GEN_tabname", 1);
+ end;
+ */
+ function _triggers($tabname, $taboptions)
{
- if (!$this->seqField) return array();
+ if (!$this->seqField) {
+ return array();
+ }
- $tab1 = preg_replace( '/"/', '', $tabname );
+ $tab1 = preg_replace('/"/', '', $tabname);
if ($this->schema) {
- $t = strpos($tab1,'.');
- if ($t !== false) $tab = substr($tab1,$t+1);
- else $tab = $tab1;
+ $t = strpos($tab1, '.');
+ if ($t !== false) {
+ $tab = substr($tab1, $t + 1);
+ } else {
+ $tab = $tab1;
+ }
$seqField = $this->seqField;
- $seqname = $this->schema.'.'.$this->seqPrefix.$tab;
- $trigname = $this->schema.'.t_'.$this->seqPrefix.$tab;
+ $seqname = $this->schema . '.' . $this->seqPrefix . $tab;
+ $trigname = $this->schema . '.t_' . $this->seqPrefix . $tab;
} else {
$seqField = $this->seqField;
- $seqname = $this->seqPrefix.$tab1;
- $trigname = 't_'.$seqname;
+ $seqname = $this->seqPrefix . $tab1;
+ $trigname = 't_' . $seqname;
}
- if (isset($tableoptions['DROP']))
- { $sql[] = "DROP GENERATOR $seqname";
- }
- elseif (isset($tableoptions['REPLACE']))
- { $sql[] = "DROP GENERATOR \"$seqname\"";
- $sql[] = "CREATE GENERATOR \"$seqname\"";
- $sql[] = "ALTER TRIGGER \"$trigname\" BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END";
- }
- else
- { $sql[] = "CREATE GENERATOR $seqname";
- $sql[] = "CREATE TRIGGER $trigname FOR $tabname BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID($seqname, 1); END";
+ if (isset($taboptions['DROP'])) {
+ $sql[] = "DROP SEQUENCE $seqname";
+ } elseif (isset($taboptions['REPLACE'])) {
+ $sql[] = "DROP SEQUENCE \"$seqname\"";
+ $sql[] = "CREATE SEQUENCE \"$seqname\"";
+ $sql[] = "ALTER TRIGGER \"$trigname\" BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END";
+ } else {
+ $sql[] = "CREATE SEQUENCE $seqname";
+ $sql[] = "CREATE TRIGGER $trigname FOR $tabname BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID($seqname, 1); END";
}
$this->seqField = false;
@@ -211,27 +239,39 @@ end;
/**
* Change the definition of one column
*
- * As some DBM's can't do that on there own, you need to supply the complete definition of the new table,
- * to allow, recreating the table and copying the content over to the new table
* @param string $tabname table-name
* @param string $flds column-name and type for the changed column
- * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
- * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default ''
+ * @param string $tableflds Unused
+ * @param array|string $tableoptions Unused
+ *
* @return array with SQL strings
*/
- function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
+ public function alterColumnSQL($tabname, $flds, $tableflds = '', $tableoptions = '')
{
- $tabname = $this->TableName ($tabname);
+ $tabname = $this->TableName($tabname);
$sql = array();
- list($lines,$pkey,$idxs) = $this->_GenFields($flds);
+ list($lines, , $idxs) = $this->_GenFields($flds);
// genfields can return FALSE at times
- if ($lines == null) $lines = array();
+
+ if ($lines == null) {
+ $lines = array();
+ }
+
$alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
- foreach($lines as $v) {
+
+ foreach ($lines as $v) {
+ /*
+ * The type must be preceded by the keyword 'TYPE'
+ */
+ $vExplode = explode(' ', $v);
+ $vExplode = array_filter($vExplode);
+ array_splice($vExplode, 1, 0, array('TYPE'));
+ $v = implode(' ', $vExplode);
$sql[] = $alter . $v;
}
+
if (is_array($idxs)) {
- foreach($idxs as $idx => $idxdef) {
+ foreach ($idxs as $idx => $idxdef) {
$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
$sql = array_merge($sql, $sql_idxs);
}
diff --git a/drivers/adodb-firebird.inc.php b/drivers/adodb-firebird.inc.php
index 09b7545c..e479077f 100644
--- a/drivers/adodb-firebird.inc.php
+++ b/drivers/adodb-firebird.inc.php
@@ -35,39 +35,113 @@ class ADODB_firebird extends ADOConnection {
var $fmtTimeStamp = "'Y-m-d, H:i:s'";
var $concat_operator='||';
var $_transactionID;
- var $metaTablesSQL = "select lower(rdb\$relation_name) from rdb\$relations where rdb\$relation_name not like 'RDB\$%'";
+
+ public $metaTablesSQL = "SELECT LOWER(rdb\$relation_name) FROM rdb\$relations";
//OPN STUFF start
+
var $metaColumnsSQL = "select lower(a.rdb\$field_name), a.rdb\$null_flag, a.rdb\$default_source, b.rdb\$field_length, b.rdb\$field_scale, b.rdb\$field_sub_type, b.rdb\$field_precision, b.rdb\$field_type from rdb\$relation_fields a, rdb\$fields b where a.rdb\$field_source = b.rdb\$field_name and a.rdb\$relation_name = '%s' order by a.rdb\$field_position asc";
//OPN STUFF end
- var $ibasetrans;
+
+ public $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s";
+
+ public $_dropSeqSQL = "DROP SEQUENCE %s";
+
var $hasGenID = true;
var $_bindInputArray = true;
- var $buffers = 0;
- var $dialect = 3;
var $sysDate = "cast('TODAY' as timestamp)";
var $sysTimeStamp = "CURRENT_TIMESTAMP"; //"cast('NOW' as timestamp)";
var $ansiOuter = true;
var $hasAffectedRows = true;
var $poorAffectedRows = false;
var $blobEncodeType = 'C';
- var $role = false;
+ /*
+ * firebird custom optionally specifies the user role
+ */
+ public $role = false;
+ /*
+ * firebird custom optionally specifies the connection buffers
+ */
+ public $buffers = 0;
+
+ /*
+ * firebird custom optionally specifies database dialect
+ */
+ public $dialect = 3;
+
var $nameQuote = ''; /// string to use to quote identifiers and names
function __construct()
{
- // Ignore IBASE_DEFAULT we want a more practical transaction!
- // if (defined('IBASE_DEFAULT')) $this->ibasetrans = IBASE_DEFAULT;
- // else
- $this->ibasetrans = IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED;
+ parent::__construct();
+ $this->setTransactionMode('');
}
+ /**
+ * Sets the isolation level of a transaction.
+ *
+ * The default behavior is a more practical IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED
+ * instead of IBASE_DEFAULT
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:settransactionmode
+ *
+ * @param string $transaction_mode The transaction mode to set.
+ *
+ * @return void
+ */
+ public function setTransactionMode($transaction_mode)
+ {
+ $this->_transmode = $transaction_mode;
+
+ if (empty($transaction_mode)) {
+ $this->_transmode = IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED;
+ }
- // returns true or false
- function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$persist=false)
+ }
+
+ /**
+ * Connect to a database.
+ *
+ * @todo add: parameter int $port, parameter string $socket
+ *
+ * @param string|null $argHostname (Optional) The host to connect to.
+ * @param string|null $argUsername (Optional) The username to connect as.
+ * @param string|null $argPassword (Optional) The password to connect with.
+ * @param string|null $argDatabasename (Optional) The name of the database to start in when connected.
+ * @param bool $persist (Optional) Whether or not to use a persistent connection.
+ *
+ * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
+ * isn't currently loaded.
+ */
+ public function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$persist=false)
{
- if (!function_exists('fbird_pconnect')) return null;
- if ($argDatabasename) $argHostname .= ':'.$argDatabasename;
+ if (!function_exists('fbird_pconnect'))
+ return null;
+
+ if ($argDatabasename)
+ $argHostname .= ':'.$argDatabasename;
+
$fn = ($persist) ? 'fbird_pconnect':'fbird_connect';
+
+ /*
+ * Now merge in the standard connection parameters setting
+ */
+ foreach ($this->connectionParameters as $options)
+ {
+ foreach($options as $k=>$v)
+ {
+ switch($k){
+ case 'role':
+ $this->role = $v;
+ break;
+ case 'dialect':
+ $this->dialect = $v;
+ break;
+ case 'buffers':
+ $this->buffers = $v;
+ }
+ }
+ }
+
if ($this->role)
$this->_connectionID = $fn($argHostname,$argUsername,$argPassword,
$this->charSet,$this->buffers,$this->dialect,$this->role);
@@ -75,40 +149,39 @@ class ADODB_firebird extends ADOConnection {
$this->_connectionID = $fn($argHostname,$argUsername,$argPassword,
$this->charSet,$this->buffers,$this->dialect);
- if ($this->dialect != 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html
- $this->replaceQuote = "''";
+ if ($this->dialect == 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html
+ $this->replaceQuote = "";
}
if ($this->_connectionID === false) {
- $this->_handleerror();
+ $this->_handleError();
return false;
}
- // PHP5 change.
- if (function_exists('fbird_timefmt')) {
- fbird_timefmt($this->fbird_datefmt,fbird_DATE );
- if ($this->dialect == 1) {
- fbird_timefmt($this->fbird_datefmt,fbird_TIMESTAMP );
- } else {
- fbird_timefmt($this->fbird_timestampfmt,fbird_TIMESTAMP );
- }
- fbird_timefmt($this->fbird_timefmt,fbird_TIME );
+ ini_set("ibase.timestampformat", $this->fbird_timestampfmt);
+ ini_set("ibase.dateformat", $this->fbird_datefmt);
+ ini_set("ibase.timeformat", $this->fbird_timefmt);
- } else {
- ini_set("ibase.timestampformat", $this->fbird_timestampfmt);
- ini_set("ibase.dateformat", $this->fbird_datefmt);
- ini_set("ibase.timeformat", $this->fbird_timefmt);
- }
return true;
}
- // returns true or false
+ /**
+ * Connect to a database with a persistent connection.
+ *
+ * @param string|null $argHostname The host to connect to.
+ * @param string|null $argUsername The username to connect as.
+ * @param string|null $argPassword The password to connect with.
+ * @param string|null $argDatabasename The name of the database to start in when connected.
+ *
+ * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
+ * isn't currently loaded.
+ */
function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
{
return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,true);
}
- function MetaPrimaryKeys($table,$owner_notused=false,$internalKey=false)
+ public function metaPrimaryKeys($table,$owner_notused=false,$internalKey=false)
{
if ($internalKey) {
return array('RDB$DB_KEY');
@@ -126,31 +199,58 @@ class ADODB_firebird extends ADOConnection {
return false;
}
- function ServerInfo()
+ /**
+ * Get information about the current Firebird server.
+ *
+ * @return array
+ */
+ public function serverInfo()
{
$arr['dialect'] = $this->dialect;
switch($arr['dialect']) {
- case '':
- case '1': $s = 'Firebird Dialect 1'; break;
- case '2': $s = 'Firebird Dialect 2'; break;
- default:
- case '3': $s = 'Firebird Dialect 3'; break;
+ case '':
+ case '1':
+ $s = 'Firebird Dialect 1';
+ break;
+ case '2':
+ $s = 'Firebird Dialect 2';
+ break;
+ default:
+ case '3':
+ $s = 'Firebird Dialect 3';
+ break;
}
$arr['version'] = ADOConnection::_findvers($s);
$arr['description'] = $s;
return $arr;
}
- function BeginTrans()
+ /**
+ * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
+ *
+ * @return bool true if succeeded or false if database does not support transactions
+ */
+ public function beginTrans()
{
if ($this->transOff) return true;
$this->transCnt += 1;
$this->autoCommit = false;
- $this->_transactionID = fbird_trans( $this->ibasetrans, $this->_connectionID );
+ /*
+ * We manage the transaction mode via fbird_trans
+ */
+ $this->_transactionID = fbird_trans( $this->_transmode, $this->_connectionID );
return $this->_transactionID;
}
- function CommitTrans($ok=true)
+
+ /**
+ * Commits a transaction
+ *
+ * @param bool $ok set to false to rollback transaction, true to commit
+ *
+ * @return true/false.
+ */
+ public function commitTrans($ok=true)
{
if (!$ok) {
return $this->RollbackTrans();
@@ -173,31 +273,26 @@ class ADODB_firebird extends ADOConnection {
function _affectedrows()
{
- return fbird_affected_rows( $this->_transactionID ? $this->_transactionID : $this->_connectionID );
+ return fbird_affected_rows( $this->_transactionID ? $this->_transactionID : $this->_connectionID );
}
- // there are some compat problems with ADODB_COUNTRECS=false and $this->_logsql currently.
- // it appears that ibase extension cannot support multiple concurrent queryid's
- function _Execute($sql,$inputarr=false) {
- global $ADODB_COUNTRECS;
-
- if ($this->_logsql) {
- $savecrecs = $ADODB_COUNTRECS;
- $ADODB_COUNTRECS = true; // force countrecs
- $ret =& ADOConnection::_Execute($sql,$inputarr);
- $ADODB_COUNTRECS = $savecrecs;
- } else {
- $ret = ADOConnection::_Execute($sql,$inputarr);
- }
- return $ret;
- }
-
- function RollbackTrans()
+ /**
+ * Rollback a smart transaction.
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rollbacktrans
+ *
+ * @return bool
+ */
+ public function rollbackTrans()
{
- if ($this->transOff) return true;
- if ($this->transCnt) $this->transCnt -= 1;
+ if ($this->transOff)
+ return true;
+ if ($this->transCnt)
+ $this->transCnt -= 1;
+
$ret = false;
$this->autoCommit = true;
+
if ($this->_transactionID) {
$ret = fbird_rollback($this->_transactionID);
}
@@ -206,16 +301,26 @@ class ADODB_firebird extends ADOConnection {
return $ret;
}
- function metaIndexes ($table, $primary = FALSE, $owner=false)
+ /**
+ * Get a list of indexes on the specified table.
+ *
+ * @param string $table The name of the table to get indexes for.
+ * @param bool $primary (Optional) Whether or not to include the primary key.
+ * @param bool $owner (Optional) Unused.
+ *
+ * @return array|bool An array of the indexes, or false if the query to get the indexes failed.
+ */
+ public function metaIndexes($table, $primary = false, $owner = false)
{
// save old fetch mode
global $ADODB_FETCH_MODE;
- $false = false;
$save = $ADODB_FETCH_MODE;
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+
if ($this->fetchMode !== FALSE) {
$savem = $this->SetFetchMode(FALSE);
}
+
$table = strtoupper($table);
$sql = "SELECT * FROM RDB\$INDICES WHERE RDB\$RELATION_NAME = '".$table."'";
if (!$primary) {
@@ -224,18 +329,19 @@ class ADODB_firebird extends ADOConnection {
$sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$FOREIGN%'";
}
// get index details
- $rs = $this->Execute($sql);
+ $rs = $this->execute($sql);
if (!is_object($rs)) {
// restore fetchmode
if (isset($savem)) {
$this->SetFetchMode($savem);
}
$ADODB_FETCH_MODE = $save;
- return $false;
+ return false;
}
$indexes = array();
while ($row = $rs->FetchRow()) {
- $index = trim(preg_replace("/[\n\r]+/",'',$row[0]));
+
+ $index = trim($row[0]);
if (!isset($indexes[$index])) {
if (is_null($row[3])) {
$row[3] = 0;
@@ -245,12 +351,13 @@ class ADODB_firebird extends ADOConnection {
'columns' => array()
);
}
- $sql = "SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '".$index."' ORDER BY RDB\$FIELD_POSITION ASC";
- $rs1 = $this->Execute($sql);
+ $sql = sprintf("SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '%s' ORDER BY RDB\$FIELD_POSITION ASC",$index);
+ $rs1 = $this->execute($sql);
while ($row1 = $rs1->FetchRow()) {
- $indexes[$index]['columns'][$row1[2]] = $row1[1];
+ $indexes[$index]['columns'][$row1[2]] = trim($row1[1]);
}
}
+
// restore fetchmode
if (isset($savem)) {
$this->SetFetchMode($savem);
@@ -260,38 +367,60 @@ class ADODB_firebird extends ADOConnection {
return $indexes;
}
-
- // See http://community.borland.com/article/0,1410,25844,00.html
- function RowLock($tables,$where,$col=false)
+ /**
+ * Lock a table row for a duration of a transaction.
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rowlock
+ * @link https://firebirdsql.org/refdocs/langrefupd21-notes-withlock.html
+ *
+ * @param string $table The table(s) to lock rows for.
+ * @param string $where (Optional) The WHERE clause to use to determine which rows to lock.
+ * @param string $col (Optional) The columns to select.
+ *
+ * @return bool True if the locking SQL statement executed successfully, otherwise false.
+ */
+ public function rowLock($table,$where,$col=false)
{
- if ($this->autoCommit) {
- $this->BeginTrans();
- }
- $this->Execute("UPDATE $table SET $col=$col WHERE $where "); // is this correct - jlim?
- return 1;
- }
+ if ($this->transCnt==0)
+ $this->beginTrans();
-
- function CreateSequence($seqname = 'adodbseq', $startID = 1)
- {
- $ok = $this->Execute(("CREATE GENERATOR $seqname" ));
- if (!$ok) return false;
- return $this->Execute("SET GENERATOR $seqname TO ".($startID-1));
+ if ($where) $where = ' where '.$where;
+ $rs = $this->execute("SELECT $col FROM $table $where FOR UPDATE WITH LOCK");
+ return !empty($rs);
}
- function DropSequence($seqname = 'adodbseq')
+ /**
+ * Creates a sequence in the database.
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:createsequence
+ *
+ * @param string $seqname The sequence name.
+ * @param int $startID The start id.
+ *
+ * @return ADORecordSet|bool A record set if executed successfully, otherwise false.
+ */
+ public function createSequence($seqname='adodbseq', $startID = 1)
{
- $seqname = strtoupper($seqname);
- return $this->Execute("DROP GENERATOR $seqname");
+ $sql = sprintf($this->_genSeqSQL,$seqname,$startID);
+ return $this->execute($sql);
}
- function GenID($seqname='adodbseq',$startID=1)
+ /**
+ * A portable method of creating sequence numbers.
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:genid
+ *
+ * @param string $seqname (Optional) The name of the sequence to use.
+ * @param int $startID (Optional) The point to start at in the sequence.
+ *
+ * @return int
+ */
+ public function genID($seqname='adodbseq',$startID=1)
{
$getnext = ("SELECT Gen_ID($seqname,1) FROM RDB\$DATABASE");
$rs = @$this->Execute($getnext);
if (!$rs) {
- $this->Execute(("CREATE GENERATOR $seqname" ));
- $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';');
+ $this->Execute("CREATE SEQUENCE $seqname START WITH $startID");
$rs = $this->Execute($getnext);
}
if ($rs && !$rs->EOF) {
@@ -308,36 +437,56 @@ class ADODB_firebird extends ADOConnection {
return $this->genID;
}
- function SelectDB($dbName)
+ function selectDB($dbName)
{
return false;
}
- function _handleerror()
+ function _handleError()
{
- $this->_errorMsg = fbird_errmsg();
+ $this->_errorCode = fbird_errcode();
+ $this->_errorMsg = fbird_errmsg();
}
- function ErrorNo()
+
+ public function errorNo()
{
- if (preg_match('/error code = ([\-0-9]*)/i', $this->_errorMsg,$arr)) return (integer) $arr[1];
- else return 0;
+ return (integer) $this->_errorCode;
}
- function ErrorMsg()
+ function errorMsg()
{
return $this->_errorMsg;
}
- function Prepare($sql)
+ /**
+ * Prepares an SQL statement and returns a handle to use.
+ * This is not used by bound parameters anymore
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:prepare
+ * @todo update this function to handle prepared statements correctly
+ *
+ * @param string $sql The SQL to prepare.
+ *
+ * @return bool|array The SQL that was provided and the prepared parameters,
+ * or false if the preparation fails
+ */
+ public function prepare($sql)
{
$stmt = fbird_prepare($this->_connectionID,$sql);
- if (!$stmt) return false;
+ if (!$stmt)
+ return false;
return array($sql,$stmt);
}
- // returns query ID if successful, otherwise false
- // there have been reports of problems with nested queries - the code is probably not re-entrant?
+ /**
+ * Return the query id.
+ *
+ * @param string|array $sql
+ * @param array $iarr
+ *
+ * @return bool|object
+ */
function _query($sql,$iarr=false)
{
if ( !$this->isConnected() ) return false;
@@ -352,7 +501,7 @@ class ADODB_firebird extends ADOConnection {
$fn = 'fbird_execute';
$sql = $sql[1];
if (is_array($iarr)) {
- if ( !isset($iarr[0]) )
+ if ( !isset($iarr[0]) )
$iarr[0] = ''; // PHP5 compat hack
$fnarr = array_merge( array($sql) , $iarr);
$ret = call_user_func_array($fn,$fnarr);
@@ -362,9 +511,9 @@ class ADODB_firebird extends ADOConnection {
}
} else {
$fn = 'fbird_query';
- if (is_array($iarr))
+ if (is_array($iarr))
{
- if (sizeof($iarr) == 0)
+ if (sizeof($iarr) == 0)
$iarr[0] = ''; // PHP5 compat hack
$fnarr = array_merge( array($conn,$sql) , $iarr);
$ret = call_user_func_array($fn,$fnarr);
@@ -377,7 +526,7 @@ class ADODB_firebird extends ADOConnection {
fbird_commit($this->_connectionID);
}
- $this->_handleerror();
+ $this->_handleError();
return $ret;
}
@@ -397,122 +546,133 @@ class ADODB_firebird extends ADOConnection {
$fld->max_length = $flen;
$fld->scale = null;
switch($ftype){
- case 7:
- case 8:
- if ($dialect3) {
- switch($fsubtype){
- case 0:
- $fld->type = ($ftype == 7 ? 'smallint' : 'integer');
- break;
- case 1:
- $fld->type = 'numeric';
- $fld->max_length = $fprecision;
- $fld->scale = $fscale;
- break;
- case 2:
- $fld->type = 'decimal';
- $fld->max_length = $fprecision;
- $fld->scale = $fscale;
- break;
- } // switch
- } else {
- if ($fscale !=0) {
+ case 7:
+ case 8:
+ if ($dialect3) {
+ switch($fsubtype){
+ case 0:
+ $fld->type = ($ftype == 7 ? 'smallint' : 'integer');
+ break;
+ case 1:
+ $fld->type = 'numeric';
+ $fld->max_length = $fprecision;
+ $fld->scale = $fscale;
+ break;
+ case 2:
$fld->type = 'decimal';
+ $fld->max_length = $fprecision;
$fld->scale = $fscale;
- $fld->max_length = ($ftype == 7 ? 4 : 9);
- } else {
- $fld->type = ($ftype == 7 ? 'smallint' : 'integer');
- }
- }
- break;
- case 16:
- if ($dialect3) {
- switch($fsubtype){
- case 0:
- $fld->type = 'decimal';
- $fld->max_length = 18;
- $fld->scale = 0;
- break;
- case 1:
- $fld->type = 'numeric';
- $fld->max_length = $fprecision;
- $fld->scale = $fscale;
- break;
- case 2:
- $fld->type = 'decimal';
- $fld->max_length = $fprecision;
- $fld->scale = $fscale;
- break;
- } // switch
- }
- break;
- case 10:
- $fld->type = 'float';
- break;
- case 14:
- $fld->type = 'char';
- break;
- case 27:
+ break;
+ } // switch
+ } else {
if ($fscale !=0) {
$fld->type = 'decimal';
- $fld->max_length = 15;
- $fld->scale = 5;
+ $fld->scale = $fscale;
+ $fld->max_length = ($ftype == 7 ? 4 : 9);
} else {
- $fld->type = 'double';
+ $fld->type = ($ftype == 7 ? 'smallint' : 'integer');
}
- break;
- case 35:
- if ($dialect3) {
- $fld->type = 'timestamp';
- } else {
- $fld->type = 'date';
- }
- break;
- case 12:
+ }
+ break;
+ case 16:
+ if ($dialect3) {
+ switch($fsubtype){
+ case 0:
+ $fld->type = 'decimal';
+ $fld->max_length = 18;
+ $fld->scale = 0;
+ break;
+ case 1:
+ $fld->type = 'numeric';
+ $fld->max_length = $fprecision;
+ $fld->scale = $fscale;
+ break;
+ case 2:
+ $fld->type = 'decimal';
+ $fld->max_length = $fprecision;
+ $fld->scale = $fscale;
+ break;
+ } // switch
+ }
+ break;
+ case 10:
+ $fld->type = 'float';
+ break;
+ case 14:
+ $fld->type = 'char';
+ break;
+ case 27:
+ if ($fscale !=0) {
+ $fld->type = 'decimal';
+ $fld->max_length = 15;
+ $fld->scale = 5;
+ } else {
+ $fld->type = 'double';
+ }
+ break;
+ case 35:
+ if ($dialect3) {
+ $fld->type = 'timestamp';
+ } else {
$fld->type = 'date';
- break;
- case 13:
- $fld->type = 'time';
- break;
- case 37:
- $fld->type = 'varchar';
- break;
- case 40:
- $fld->type = 'cstring';
- break;
- case 261:
- $fld->type = 'blob';
- $fld->max_length = -1;
- break;
+ }
+ break;
+ case 12:
+ $fld->type = 'date';
+ break;
+ case 13:
+ $fld->type = 'time';
+ break;
+ case 37:
+ $fld->type = 'varchar';
+ break;
+ case 40:
+ $fld->type = 'cstring';
+ break;
+ case 261:
+ $fld->type = 'blob';
+ $fld->max_length = -1;
+ break;
} // switch
}
//OPN STUFF end
- // returns array of ADOFieldObjects for current table
- function MetaColumns($table, $normalize=true)
+ /**
+ * Return an array of information about a table's columns.
+ *
+ * @param string $table The name of the table to get the column info for.
+ * @param bool $normalize (Optional) Unused.
+ *
+ * @return ADOFieldObject[]|bool An array of info for each column,
+ * or false if it could not determine the info.
+ */
+ public function metaColumns($table, $normalize = true)
{
- global $ADODB_FETCH_MODE;
+
+ global $ADODB_FETCH_MODE;
$save = $ADODB_FETCH_MODE;
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
- $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
+ $rs = $this->execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
$ADODB_FETCH_MODE = $save;
- $false = false;
+
if ($rs === false) {
- return $false;
+ return false;
}
$retarr = array();
//OPN STUFF start
- $dialect3 = ($this->dialect==3 ? true : false);
+ $dialect3 = $this->dialect == 3;
//OPN STUFF end
while (!$rs->EOF) { //print_r($rs->fields);
$fld = new ADOFieldObject();
$fld->name = trim($rs->fields[0]);
//OPN STUFF start
- $this->_ConvertFieldType($fld, $rs->fields[7], $rs->fields[3], $rs->fields[4], $rs->fields[5], $rs->fields[6], $dialect3);
+ //print_r($rs->fields);
+ $this->_ConvertFieldType(
+ $fld, $rs->fields[7], $rs->fields[3], $rs->fields[4], $rs->fields[5], $rs->fields[6], $dialect3);
if (isset($rs->fields[1]) && $rs->fields[1]) {
$fld->not_null = true;
}
@@ -520,17 +680,24 @@ class ADODB_firebird extends ADOConnection {
$fld->has_default = true;
$d = substr($rs->fields[2],strlen('default '));
- switch ($fld->type)
- {
- case 'smallint':
- case 'integer': $fld->default_value = (int) $d; break;
- case 'char':
- case 'blob':
- case 'text':
- case 'varchar': $fld->default_value = (string) substr($d,1,strlen($d)-2); break;
- case 'double':
- case 'float': $fld->default_value = (float) $d; break;
- default: $fld->default_value = $d; break;
+ switch ($fld->type) {
+ case 'smallint':
+ case 'integer':
+ $fld->default_value = (int)$d;
+ break;
+ case 'char':
+ case 'blob':
+ case 'text':
+ case 'varchar':
+ $fld->default_value = (string)substr($d, 1, strlen($d) - 2);
+ break;
+ case 'double':
+ case 'float':
+ $fld->default_value = (float)$d;
+ break;
+ default:
+ $fld->default_value = $d;
+ break;
}
// case 35:$tt = 'TIMESTAMP'; break;
}
@@ -546,40 +713,79 @@ class ADODB_firebird extends ADOConnection {
$rs->MoveNext();
}
$rs->Close();
- if ( empty($retarr)) return $false;
+ if ( empty($retarr))
+ return false;
else return $retarr;
}
- function BlobEncode( $blob )
+ /**
+ * Retrieves a list of tables based on given criteria
+ *
+ * @param string|bool $ttype (Optional) Table type = 'TABLE', 'VIEW' or false=both (default)
+ * @param string|bool $showSchema (Optional) schema name, false = current schema (default)
+ * @param string|bool $mask (Optional) filters the table by name
+ *
+ * @return array list of tables
+ */
+ public function metaTables($ttype = false, $showSchema = false, $mask = false)
+ {
+ $save = $this->metaTablesSQL;
+ if (!$showSchema) {
+ $this->metaTablesSQL .= " WHERE (rdb\$relation_name NOT LIKE 'RDB\$%' AND rdb\$relation_name NOT LIKE 'MON\$%' AND rdb\$relation_name NOT LIKE 'SEC\$%')";
+ } elseif (is_string($showSchema)) {
+ $this->metaTablesSQL .= $this->qstr($showSchema);
+ }
+
+ if ($mask) {
+ $mask = $this->qstr($mask);
+ $this->metaTablesSQL .= " AND table_name LIKE $mask";
+ }
+ $ret = ADOConnection::metaTables($ttype,$showSchema);
+
+ $this->metaTablesSQL = $save;
+ return $ret;
+ }
+
+ /**
+ * Encodes a blob, then assigns an id ready to be used
+ *
+ * @param string $blob The blob to be encoded
+ *
+ * @return bool success
+ */
+ public function blobEncode( $blob )
{
$blobid = fbird_blob_create( $this->_connectionID);
fbird_blob_add( $blobid, $blob );
return fbird_blob_close( $blobid );
}
- // since we auto-decode all blob's since 2.42,
- // BlobDecode should not do any transforms
- function BlobDecode($blob)
+ /**
+ * Manually decode a blob
+ *
+ * since we auto-decode all blob's since 2.42,
+ * BlobDecode should not do any transforms
+ *
+ * @param string $blob
+ *
+ * @return string the same blob
+ */
+ public function blobDecode($blob)
{
return $blob;
}
- // old blobdecode function
- // still used to auto-decode all blob's
- function _BlobDecode_old( $blob )
- {
- $blobid = fbird_blob_open($this->_connectionID, $blob );
- $realblob = fbird_blob_get( $blobid,$this->maxblobsize); // 2nd param is max size of blob -- Kevin Boillet <kevinboillet@yahoo.fr>
- while($string = fbird_blob_get($blobid, 8192)){
- $realblob .= $string;
- }
- fbird_blob_close( $blobid );
- return( $realblob );
- }
-
- function _BlobDecode( $blob )
+ /**
+ * Auto function called on read of blob to decode
+ *
+ * @param string $blob Value to decode
+ *
+ * @return string Decoded blob
+ */
+ public function _blobDecode( $blob )
{
+
$blob_data = fbird_blob_info($this->_connectionID, $blob );
$blobid = fbird_blob_open($this->_connectionID, $blob );
@@ -597,10 +803,24 @@ class ADODB_firebird extends ADOConnection {
return( $realblob );
}
- function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
+ /**
+ * Insert blob data into a database column directly
+ * from file
+ *
+ * @param string $table table to insert
+ * @param string $column column to insert
+ * @param string $path physical file name
+ * @param string $where string to find unique record
+ * @param string $blobtype BLOB or CLOB
+ *
+ * @return bool success
+ */
+ public function updateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
{
$fd = fopen($path,'rb');
- if ($fd === false) return false;
+ if ($fd === false)
+ return false;
+
$blob_id = fbird_blob_create($this->_connectionID);
/* fill with data */
@@ -616,105 +836,106 @@ class ADODB_firebird extends ADOConnection {
return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
}
- /*
- Insert a null into the blob field of the table first.
- Then use UpdateBlob to store the blob.
-
- Usage:
-
- $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
- $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
- */
- function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+ /**
+ * Insert blob data into a database column
+ *
+ * @param string $table table to insert
+ * @param string $column column to insert
+ * @param string $val value to insert
+ * @param string $where string to find unique record
+ * @param string $blobtype BLOB or CLOB
+ *
+ * @return bool success
+ */
+ public function updateBlob($table,$column,$val,$where,$blobtype='BLOB')
{
- $blob_id = fbird_blob_create($this->_connectionID);
+ $blob_id = fbird_blob_create($this->_connectionID);
- // fbird_blob_add($blob_id, $val);
+ // fbird_blob_add($blob_id, $val);
- // replacement that solves the problem by which only the first modulus 64K /
- // of $val are stored at the blob field ////////////////////////////////////
- // Thx Abel Berenstein aberenstein#afip.gov.ar
- $len = strlen($val);
- $chunk_size = 32768;
- $tail_size = $len % $chunk_size;
- $n_chunks = ($len - $tail_size) / $chunk_size;
+ // replacement that solves the problem by which only the first modulus 64K /
+ // of $val are stored at the blob field ////////////////////////////////////
+ // Thx Abel Berenstein aberenstein#afip.gov.ar
+ $len = strlen($val);
+ $chunk_size = 32768;
+ $tail_size = $len % $chunk_size;
+ $n_chunks = ($len - $tail_size) / $chunk_size;
- for ($n = 0; $n < $n_chunks; $n++) {
- $start = $n * $chunk_size;
- $data = substr($val, $start, $chunk_size);
- fbird_blob_add($blob_id, $data);
- }
+ for ($n = 0; $n < $n_chunks; $n++) {
+ $start = $n * $chunk_size;
+ $data = substr($val, $start, $chunk_size);
+ fbird_blob_add($blob_id, $data);
+ }
- if ($tail_size) {
- $start = $n_chunks * $chunk_size;
- $data = substr($val, $start, $tail_size);
- fbird_blob_add($blob_id, $data);
- }
- // end replacement /////////////////////////////////////////////////////////
+ if ($tail_size) {
+ $start = $n_chunks * $chunk_size;
+ $data = substr($val, $start, $tail_size);
+ fbird_blob_add($blob_id, $data);
+ }
+ // end replacement /////////////////////////////////////////////////////////
- $blob_id_str = fbird_blob_close($blob_id);
+ $blob_id_str = fbird_blob_close($blob_id);
- return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
+ return $this->execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
}
- function OldUpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+ /**
+ * Returns a portably-formatted date string from a timestamp database column.
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:sqldate
+ *
+ * Firebird does not support an AM/PM format, so the A indicator always shows AM
+ *
+ * @param string $fmt The date format to use.
+ * @param string|bool $col (Optional) The table column to date format, or if false, use NOW().
+ *
+ * @return string The SQL DATE_FORMAT() string, or empty if the provided date format was empty.
+ */
+ public function sqlDate($fmt, $col=false)
{
- $blob_id = fbird_blob_create($this->_connectionID);
- fbird_blob_add($blob_id, $val);
- $blob_id_str = fbird_blob_close($blob_id);
- return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
- }
+ if (!$col)
+ $col = 'CURRENT_TIMESTAMP';
- // Format date column in sql string given an input format that understands Y M D
- // Only since Interbase 6.0 - uses EXTRACT
- // problem - does not zero-fill the day and month yet
- function SQLDate($fmt, $col=false)
- {
- if (!$col) $col = $this->sysDate;
$s = '';
$len = strlen($fmt);
for ($i=0; $i < $len; $i++) {
if ($s) $s .= '||';
$ch = $fmt[$i];
- switch($ch) {
+ $choice = strtoupper($ch);
+ switch($choice) {
case 'Y':
- case 'y':
- $s .= "extract(year from $col)";
+ $s .= "EXTRACT(YEAR FROM $col)";
break;
case 'M':
- case 'm':
- $s .= "extract(month from $col)";
+ $s .= "RIGHT('0' || TRIM(EXTRACT(MONTH FROM $col)),2)";
break;
case 'W':
- case 'w':
// The more accurate way of doing this is with a stored procedure
// See http://wiki.firebirdsql.org/wiki/index.php?page=DATE+Handling+Functions for details
- $s .= "((extract(yearday from $col) - extract(weekday from $col - 1) + 7) / 7)";
+ $s .= "((EXTRACT(YEARDAY FROM $col) - EXTRACT(WEEKDAY FROM $col - 1) + 7) / 7)";
break;
case 'Q':
- case 'q':
- $s .= "cast(((extract(month from $col)+2) / 3) as integer)";
+ $s .= "CAST(((EXTRACT(MONTH FROM $col)+2) / 3) AS INTEGER)";
break;
case 'D':
- case 'd':
- $s .= "(extract(day from $col))";
+ $s .= "RIGHT('0' || TRIM(EXTRACT(DAY FROM $col)),2)";
break;
case 'H':
- case 'h':
- $s .= "(extract(hour from $col))";
+ $s .= "RIGHT('0' || TRIM(EXTRACT(HOUR FROM $col)),2)";
break;
case 'I':
- case 'i':
- $s .= "(extract(minute from $col))";
+ $s .= "RIGHT('0' || TRIM(EXTRACT(MINUTE FROM $col)),2)";
break;
case 'S':
- case 's':
- $s .= "CAST((extract(second from $col)) AS INTEGER)";
+ //$s .= "CAST((EXTRACT(SECOND FROM $col)) AS INTEGER)";
+ $s .= "RIGHT('0' || TRIM(EXTRACT(SECOND FROM $col)),2)";
+ break;
+ case 'A':
+ $s .= $this->qstr('AM');
break;
-
default:
if ($ch == '\\') {
$i++;
@@ -727,10 +948,44 @@ class ADODB_firebird extends ADOConnection {
return $s;
}
+ /**
+ * Creates a portable date offset field, for use in SQL statements.
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:offsetdate
+ *
+ * @param float $dayFraction A day in floating point
+ * @param string|bool $date (Optional) The date to offset. If false, uses CURDATE()
+ *
+ * @return string
+ */
+ public function offsetDate($dayFraction, $date=false)
+ {
+ if (!$date)
+ $date = $this->sysTimeStamp;
+
+ $fraction = $dayFraction * 24 * 3600;
+ return sprintf("DATEADD (second, %s, %s) FROM RDB\$DATABASE",$fraction,$date);
+ }
+
+
// Note that Interbase 6.5 uses this ROWS instead - don't you love forking wars!
// SELECT col1, col2 FROM table ROWS 5 -- get 5 rows
// SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2
- function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $secs=0)
+ /**
+ * Executes a provided SQL statement and returns a handle to the result, with the ability to supply a starting
+ * offset and record count.
+ *
+ * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:selectlimit
+ *
+ * @param string $sql The SQL to execute.
+ * @param int $nrows (Optional) The limit for the number of records you want returned. By default, all results.
+ * @param int $offset (Optional) The offset to use when selecting the results. By default, no offset.
+ * @param array|bool $inputarr (Optional) Any parameter values required by the SQL statement, or false if none.
+ * @param int $secs2cache (Optional) If greater than 0, perform a cached execute. By default, normal execution.
+ *
+ * @return ADORecordSet|false The query results, or false if the query failed to execute.
+ */
+ public function selectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $secs2cache=0)
{
$nrows = (integer) $nrows;
$offset = (integer) $offset;
@@ -739,46 +994,89 @@ class ADODB_firebird extends ADOConnection {
$str .=($offset>=0) ? "SKIP $offset " : '';
$sql = preg_replace('/^[ \t]*select/i',$str,$sql);
- if ($secs)
- $rs = $this->CacheExecute($secs,$sql,$inputarr);
+ if ($secs2cache)
+ $rs = $this->cacheExecute($secs2cache,$sql,$inputarr);
else
- $rs = $this->Execute($sql,$inputarr);
+ $rs = $this->execute($sql,$inputarr);
return $rs;
}
}
-/*--------------------------------------------------------------------------------------
- Class Name: Recordset
---------------------------------------------------------------------------------------*/
-
-class ADORecordset_firebird extends ADORecordSet
+/**
+ * Class ADORecordset_firebird
+ */
+class ADORecordset_firebird extends ADORecordSet
{
-
var $databaseType = "firebird";
- var $bind=false;
- var $_cacheType;
+ var $bind = false;
+
+ /**
+ * @var ADOFieldObject[] Holds a cached version of the metadata
+ */
+ 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();
- function __construct($id,$mode=false)
+ /**
+ * @var bool Flag to indicate if the result has a blob
+ */
+ private $fieldObjectsHaveBlob = false;
+
+ function __construct($id, $mode = false)
{
- global $ADODB_FETCH_MODE;
+ global $ADODB_FETCH_MODE;
- $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode;
- parent::__construct($id);
+ $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode;
+ parent::__construct($id);
}
+
/**
- * Get column information in the Recordset object.
- * fetchField() can be used in order to obtain information about fields in
- * a certain query result. If the field offset isn't specified, the next
- * field that wasn't yet retrieved by fetchField() is retrieved.
- * @return object containing field information.
- */
- function FetchField($fieldOffset = -1)
+ * Returns: an object containing field information.
+ *
+ * Get column information in the Recordset object. fetchField()
+ * can be used in order to obtain information about fields in a
+ * certain query result. If the field offset isn't specified,
+ * the next field that wasn't yet retrieved by fetchField()
+ * is retrieved.
+ *
+ * $param int $fieldOffset (optional default=-1 for all
+ * @return mixed an ADOFieldObject, or array of objects
+ */
+ private function _fetchField($fieldOffset = -1)
{
+ if ($this->fieldObjectsRetrieved) {
+ if ($this->fieldObjects) {
+ // Already got the information
+ if ($fieldOffset == -1) {
+ return $this->fieldObjects;
+ } else {
+ return $this->fieldObjects[$fieldOffset];
+ }
+ } else {
+ // No metadata available
+ return false;
+ }
+ }
+
+ $this->fieldObjectsRetrieved = true;
+ $this->fieldObjectsHaveBlob = false;
+
+ $this->_numOfFields = fbird_num_fields($this->_queryID);
+ for ($fieldOffset = 0; $fieldOffset < $this->_numOfFields; $fieldOffset++) {
+
$fld = new ADOFieldObject;
- $ibf = fbird_field_info($this->_queryID,$fieldOffset);
+ $ibf = fbird_field_info($this->_queryID, $fieldOffset);
$name = empty($ibf['alias']) ? $ibf['name'] : $ibf['alias'];
@@ -798,23 +1096,55 @@ class ADORecordset_firebird extends ADORecordSet
$fld->type = $ibf['type'];
$fld->max_length = $ibf['length'];
- /* This needs to be populated from the metadata */
+ // This needs to be populated from the metadata
$fld->not_null = false;
$fld->has_default = false;
$fld->default_value = 'null';
- return $fld;
+
+ $this->fieldObjects[$fieldOffset] = $fld;
+
+ $this->fieldObjectsIndex[$fld->name] = $fieldOffset;
+
+ if ($fld->type == 'BLOB') {
+ $this->fieldObjectsHaveBlob = true;
+ }
+ }
+
+ if ($fieldOffset == -1) {
+ return $this->fieldObjects;
+ }
+
+ return $this->fieldObjects[$fieldOffset];
+ }
+
+ /**
+ * Fetchfield copies the oracle method, it loads the field information
+ * into the _fieldobjs array once, to save multiple calls to the
+ * fbird_ function
+ *
+ * @param int $fieldOffset (optional)
+ *
+ * @return adoFieldObject|false
+ */
+ public function fetchField($fieldOffset = -1)
+ {
+ if ($fieldOffset == -1) {
+ return $this->fieldObjects;
+ }
+
+ return $this->fieldObjects[$fieldOffset];
}
function _initrs()
{
$this->_numOfRows = -1;
- $this->_numOfFields = @fbird_num_fields($this->_queryID);
- // cache types for blob decode check
- for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
- $f1 = $this->FetchField($i);
- $this->_cacheType[] = $f1->type;
- }
+ /*
+ * Retrieve all of the column information first. We copy
+ * this method from oracle
+ */
+ $this->_fetchField();
+
}
function _seek($row)
@@ -822,9 +1152,30 @@ class ADORecordset_firebird extends ADORecordSet
return false;
}
- function _fetch()
+ public function _fetch()
{
- $f = @fbird_fetch_row($this->_queryID);
+ $localNumeric = true;
+ if ($this->fetchMode & ADODB_FETCH_ASSOC) {
+ // Handle either associative or fetch both
+ $localNumeric = false;
+
+ $f = @fbird_fetch_assoc($this->_queryID);
+ if (is_array($f)) {
+ // Optimally do the case_upper or case_lower
+ if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) {
+ $f = array_change_key_case($f, CASE_LOWER);
+ } else {
+ if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER) {
+ $f = array_change_key_case($f, CASE_UPPER);
+ }
+ }
+
+ }
+ } else {
+ // Numeric result
+ $f = @fbird_fetch_row($this->_queryID);
+ }
+
if ($f === false) {
$this->fields = false;
return false;
@@ -833,94 +1184,143 @@ class ADORecordset_firebird extends ADORecordSet
// fix missing nulls and decode blobs automatically
global $ADODB_ANSI_PADDING_OFF;
- //$ADODB_ANSI_PADDING_OFF=1;
$rtrim = !empty($ADODB_ANSI_PADDING_OFF);
-
- for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
- if ($this->_cacheType[$i]=="BLOB") {
- if (isset($f[$i]))
- {
- $f[$i] = $this->connection->_BlobDecode($f[$i]);
+
+ /*
+ * Fix missing nulls, not sure why they would be missing
+ *
+ *
+ $nullTemplate = array_fill(0,$this->_numOfFields,null);
+ */
+ /*
+ * Retrieved record is always numeric, use array_replace
+ * to inject missing keys
+ */
+ //$f = array_replace($nullTemplate,$f);
+
+ /*
+ * For optimal performance, only process if there
+ * is a possiblity of something to do
+ */
+ if ($this->fieldObjectsHaveBlob || $rtrim) {
+ /*
+ * Create a closure for an efficient method of
+ * iterating over the elements
+ */
+ $localFieldObjects = $this->fieldObjects;
+ $localFieldObjectIndex = $this->fieldObjectsIndex;
+ $localConnection = &$this->connection;
+
+ $rowTransform = function ($value, $key) use (
+ &$f,
+ $rtrim,
+ $localFieldObjects,
+ $localConnection,
+ $localNumeric,
+ $localFieldObjectIndex
+ ) {
+ if ($localNumeric) {
+ $localKey = $key;
} else {
- $f[$i] = null;
+ // Cross reference the associative key back to numeric
+ $localKey = $localFieldObjectIndex[strtolower($key)];
}
- } else {
- if (!isset($f[$i])) {
- $f[$i] = null;
- } else if ($rtrim && is_string($f[$i])) {
- $f[$i] = rtrim($f[$i]);
+
+ // As we iterate the elements check for blobs and padding
+ if ($localFieldObjects[$localKey]->type == 'BLOB') {
+ $f[$key] = $localConnection->_BlobDecode($value);
+ } else {
+ if ($rtrim && is_string($value)) {
+ $f[$key] = rtrim($value);
+ }
}
- }
+
+ };
+ // Walk the array, applying the above closure
+ array_walk($f, $rowTransform);
}
- // OPN stuff end
- $this->fields = $f;
- if ($this->fetchMode == ADODB_FETCH_ASSOC) {
- $this->fields = $this->GetRowAssoc();
- } else if ($this->fetchMode == ADODB_FETCH_BOTH) {
- $this->fields = array_merge($this->fields,$this->GetRowAssoc());
+ if (!$localNumeric && $this->fetchMode & ADODB_FETCH_NUM) {
+ // Creates a fetch both
+ $fNum = array_values($f);
+ $f = array_merge($f, $fNum);
}
+
+ $this->fields = $f;
+
return true;
}
- /* Use associative array to get fields array */
- function Fields($colname)
+ /**
+ * Get the value of a field in the current row by column name.
+ * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
+ *
+ * @param string $colname is the field to access
+ *
+ * @return mixed the value of $colname column
+ */
+ public function fields($colname)
{
- if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
+ if ($this->fetchMode & ADODB_FETCH_ASSOC) {
+ return $this->fields[$colname];
+ }
+
if (!$this->bind) {
- $this->bind = array();
- for ($i=0; $i < $this->_numOfFields; $i++) {
- $o = $this->FetchField($i);
- $this->bind[strtoupper($o->name)] = $i;
- }
+ // fieldsObjectIndex populated by the recordset load
+ $this->bind = array_change_key_case($this->fieldObjectsIndex, CASE_UPPER);
}
return $this->fields[$this->bind[strtoupper($colname)]];
-
}
function _close()
{
- return @fbird_free_result($this->_queryID);
+ return @fbird_free_result($this->_queryID);
}
- function MetaType($t,$len=-1,$fieldobj=false)
+ public function metaType($t, $len = -1, $fieldobj = false)
{
if (is_object($t)) {
$fieldobj = $t;
$t = $fieldobj->type;
$len = $fieldobj->max_length;
}
-
+
$t = strtoupper($t);
-
- if (array_key_exists($t,$this->connection->customActualTypes))
- return $this->connection->customActualTypes[$t];
- switch ($t) {
+ if (array_key_exists($t, $this->connection->customActualTypes)) {
+ return $this->connection->customActualTypes[$t];
+ }
- case 'CHAR':
- return 'C';
+ switch ($t) {
+ case 'CHAR':
+ return 'C';
- case 'TEXT':
- case 'VARCHAR':
- case 'VARYING':
- if ($len <= $this->blobSize) return 'C';
- return 'X';
- case 'BLOB':
- return 'B';
+ case 'TEXT':
+ case 'VARCHAR':
+ case 'VARYING':
+ if ($len <= $this->blobSize) {
+ return 'C';
+ }
+ return 'X';
+ case 'BLOB':
+ return 'B';
- case 'TIMESTAMP':
- case 'DATE': return 'D';
- case 'TIME': return 'T';
- //case 'T': return 'T';
+ case 'TIMESTAMP':
+ case 'DATE':
+ return 'D';
+ case 'TIME':
+ return 'T';
+ //case 'T': return 'T';
- //case 'L': return 'L';
- case 'INT':
- case 'SHORT':
- case 'INTEGER': return 'I';
- default: return ADODB_DEFAULT_METATYPE;
+ //case 'L': return 'L';
+ case 'INT':
+ case 'SHORT':
+ case 'INTEGER':
+ return 'I';
+ default:
+ return ADODB_DEFAULT_METATYPE;
}
}