Хранилища Subversion ant

Сравнить редакции

Не учитывать пробелы Редакция 98 → Редакция 2

/trunk/lib/DB/dbase.php
Новый файл
0,0 → 1,510
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's dbase extension
* for interacting with dBase databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Tomas V.V. Cox <cox@idecnet.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: dbase.php,v 1.45 2007/09/21 13:40:41 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's dbase extension
* for interacting with dBase databases
*
* These methods overload the ones declared in DB_common.
*
* @category Database
* @package DB
* @author Tomas V.V. Cox <cox@idecnet.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_dbase extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'dbase';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'dbase';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => false,
'new_link' => false,
'numrows' => true,
'pconnect' => false,
'prepare' => false,
'ssl' => false,
'transactions' => false,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* A means of emulating result resources
* @var array
*/
var $res_row = array();
 
/**
* The quantity of results so far
*
* For emulating result resources.
*
* @var integer
*/
var $result = 0;
 
/**
* Maps dbase data type id's to human readable strings
*
* The human readable values are based on the output of PHP's
* dbase_get_header_info() function.
*
* @var array
* @since Property available since Release 1.7.0
*/
var $types = array(
'C' => 'character',
'D' => 'date',
'L' => 'boolean',
'M' => 'memo',
'N' => 'number',
);
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_dbase()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database and create it if it doesn't exist
*
* Don't call this method directly. Use DB::connect() instead.
*
* PEAR DB's dbase driver supports the following extra DSN options:
* + mode An integer specifying the read/write mode to use
* (0 = read only, 1 = write only, 2 = read/write).
* Available since PEAR DB 1.7.0.
* + fields An array of arrays that PHP's dbase_create() function needs
* to create a new database. This information is used if the
* dBase file specified in the "database" segment of the DSN
* does not exist. For more info, see the PHP manual's
* {@link http://php.net/dbase_create dbase_create()} page.
* Available since PEAR DB 1.7.0.
*
* Example of how to connect and establish a new dBase file if necessary:
* <code>
* require_once 'DB.php';
*
* $dsn = array(
* 'phptype' => 'dbase',
* 'database' => '/path/and/name/of/dbase/file',
* 'mode' => 2,
* 'fields' => array(
* array('a', 'N', 5, 0),
* array('b', 'C', 40),
* array('c', 'C', 255),
* array('d', 'C', 20),
* ),
* );
* $options = array(
* 'debug' => 2,
* 'portability' => DB_PORTABILITY_ALL,
* );
*
* $db = DB::connect($dsn, $options);
* if (PEAR::isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('dbase')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
/*
* Turn track_errors on for entire script since $php_errormsg
* is the only way to find errors from the dbase extension.
*/
@ini_set('track_errors', 1);
$php_errormsg = '';
 
if (!file_exists($dsn['database'])) {
$this->dsn['mode'] = 2;
if (empty($dsn['fields']) || !is_array($dsn['fields'])) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
'the dbase file does not exist and '
. 'it could not be created because '
. 'the "fields" element of the DSN '
. 'is not properly set');
}
$this->connection = @dbase_create($dsn['database'],
$dsn['fields']);
if (!$this->connection) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
'the dbase file does not exist and '
. 'the attempt to create it failed: '
. $php_errormsg);
}
} else {
if (!isset($this->dsn['mode'])) {
$this->dsn['mode'] = 0;
}
$this->connection = @dbase_open($dsn['database'],
$this->dsn['mode']);
if (!$this->connection) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$php_errormsg);
}
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @dbase_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ &query()
 
function &query($query = null)
{
// emulate result resources
$this->res_row[(int)$this->result] = 0;
$tmp = new DB_result($this, $this->result++);
return $tmp;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum === null) {
$rownum = $this->res_row[(int)$result]++;
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @dbase_get_record_with_names($this->connection, $rownum);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @dbase_get_record($this->connection, $rownum);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set.
*
* This method is a no-op for dbase, as there aren't result resources in
* the same sense as most other database backends.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return true;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($foo)
{
return @dbase_numfields($this->connection);
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($foo)
{
return @dbase_numrecords($this->connection);
}
 
// }}}
// {{{ quoteBoolean()
 
/**
* Formats a boolean value for use within a query in a locale-independent
* manner.
*
* @param boolean the boolean value to be quoted.
* @return string the quoted string.
* @see DB_common::quoteSmart()
* @since Method available since release 1.7.8.
*/
function quoteBoolean($boolean) {
return $boolean ? 'T' : 'F';
}
// }}}
// {{{ tableInfo()
 
/**
* Returns information about the current database
*
* @param mixed $result THIS IS UNUSED IN DBASE. The current database
* is examined regardless of what is provided here.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.7.0
*/
function tableInfo($result = null, $mode = null)
{
if (function_exists('dbase_get_header_info')) {
$id = @dbase_get_header_info($this->connection);
if (!$id && $php_errormsg) {
return $this->raiseError(DB_ERROR,
null, null, null,
$php_errormsg);
}
} else {
/*
* This segment for PHP 4 is loosely based on code by
* Hadi Rusiah <deegos@yahoo.com> in the comments on
* the dBase reference page in the PHP manual.
*/
$db = @fopen($this->dsn['database'], 'r');
if (!$db) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$php_errormsg);
}
 
$id = array();
$i = 0;
 
$line = fread($db, 32);
while (!feof($db)) {
$line = fread($db, 32);
if (substr($line, 0, 1) == chr(13)) {
break;
} else {
$pos = strpos(substr($line, 0, 10), chr(0));
$pos = ($pos == 0 ? 10 : $pos);
$id[$i] = array(
'name' => substr($line, 0, $pos),
'type' => $this->types[substr($line, 11, 1)],
'length' => ord(substr($line, 16, 1)),
'precision' => ord(substr($line, 17, 1)),
);
}
$i++;
}
 
fclose($db);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$res = array();
$count = count($id);
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => $this->dsn['database'],
'name' => $case_func($id[$i]['name']),
'type' => $id[$i]['type'],
'len' => $id[$i]['length'],
'flags' => ''
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
return $res;
}
 
// }}}
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/mysqli.php
Новый файл
0,0 → 1,1092
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's mysqli extension
* for interacting with MySQL databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: mysqli.php,v 1.82 2007/09/21 13:40:41 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's mysqli extension
* for interacting with MySQL databases
*
* This is for MySQL versions 4.1 and above. Requires PHP 5.
*
* Note that persistent connections no longer exist.
*
* These methods overload the ones declared in DB_common.
*
* @category Database
* @package DB
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
* @since Class functional since Release 1.6.3
*/
class DB_mysqli extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'mysqli';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'mysqli';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'alter',
'new_link' => false,
'numrows' => true,
'pconnect' => false,
'prepare' => false,
'ssl' => true,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
1004 => DB_ERROR_CANNOT_CREATE,
1005 => DB_ERROR_CANNOT_CREATE,
1006 => DB_ERROR_CANNOT_CREATE,
1007 => DB_ERROR_ALREADY_EXISTS,
1008 => DB_ERROR_CANNOT_DROP,
1022 => DB_ERROR_ALREADY_EXISTS,
1044 => DB_ERROR_ACCESS_VIOLATION,
1046 => DB_ERROR_NODBSELECTED,
1048 => DB_ERROR_CONSTRAINT,
1049 => DB_ERROR_NOSUCHDB,
1050 => DB_ERROR_ALREADY_EXISTS,
1051 => DB_ERROR_NOSUCHTABLE,
1054 => DB_ERROR_NOSUCHFIELD,
1061 => DB_ERROR_ALREADY_EXISTS,
1062 => DB_ERROR_ALREADY_EXISTS,
1064 => DB_ERROR_SYNTAX,
1091 => DB_ERROR_NOT_FOUND,
1100 => DB_ERROR_NOT_LOCKED,
1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
1142 => DB_ERROR_ACCESS_VIOLATION,
1146 => DB_ERROR_NOSUCHTABLE,
1216 => DB_ERROR_CONSTRAINT,
1217 => DB_ERROR_CONSTRAINT,
1356 => DB_ERROR_DIVZERO,
1451 => DB_ERROR_CONSTRAINT,
1452 => DB_ERROR_CONSTRAINT,
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
var $autocommit = true;
 
/**
* The quantity of transactions begun
*
* {@internal While this is private, it can't actually be designated
* private in PHP 5 because it is directly accessed in the test suite.}}
*
* @var integer
* @access private
*/
var $transaction_opcount = 0;
 
/**
* The database specified in the DSN
*
* It's a fix to allow calls to different databases in the same script.
*
* @var string
* @access private
*/
var $_db = '';
 
/**
* Array for converting MYSQLI_*_FLAG constants to text values
* @var array
* @access public
* @since Property available since Release 1.6.5
*/
var $mysqli_flags = array(
MYSQLI_NOT_NULL_FLAG => 'not_null',
MYSQLI_PRI_KEY_FLAG => 'primary_key',
MYSQLI_UNIQUE_KEY_FLAG => 'unique_key',
MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key',
MYSQLI_BLOB_FLAG => 'blob',
MYSQLI_UNSIGNED_FLAG => 'unsigned',
MYSQLI_ZEROFILL_FLAG => 'zerofill',
MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment',
MYSQLI_TIMESTAMP_FLAG => 'timestamp',
MYSQLI_SET_FLAG => 'set',
// MYSQLI_NUM_FLAG => 'numeric', // unnecessary
// MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie
MYSQLI_GROUP_FLAG => 'group_by'
);
 
/**
* Array for converting MYSQLI_TYPE_* constants to text values
* @var array
* @access public
* @since Property available since Release 1.6.5
*/
var $mysqli_types = array(
MYSQLI_TYPE_DECIMAL => 'decimal',
MYSQLI_TYPE_TINY => 'tinyint',
MYSQLI_TYPE_SHORT => 'int',
MYSQLI_TYPE_LONG => 'int',
MYSQLI_TYPE_FLOAT => 'float',
MYSQLI_TYPE_DOUBLE => 'double',
// MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it
MYSQLI_TYPE_TIMESTAMP => 'timestamp',
MYSQLI_TYPE_LONGLONG => 'bigint',
MYSQLI_TYPE_INT24 => 'mediumint',
MYSQLI_TYPE_DATE => 'date',
MYSQLI_TYPE_TIME => 'time',
MYSQLI_TYPE_DATETIME => 'datetime',
MYSQLI_TYPE_YEAR => 'year',
MYSQLI_TYPE_NEWDATE => 'date',
MYSQLI_TYPE_ENUM => 'enum',
MYSQLI_TYPE_SET => 'set',
MYSQLI_TYPE_TINY_BLOB => 'tinyblob',
MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',
MYSQLI_TYPE_LONG_BLOB => 'longblob',
MYSQLI_TYPE_BLOB => 'blob',
MYSQLI_TYPE_VAR_STRING => 'varchar',
MYSQLI_TYPE_STRING => 'char',
MYSQLI_TYPE_GEOMETRY => 'geometry',
/* These constants are conditionally compiled in ext/mysqli, so we'll
* define them by number rather than constant. */
16 => 'bit',
246 => 'decimal',
);
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_mysqli()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* PEAR DB's mysqli driver supports the following extra DSN options:
* + When the 'ssl' $option passed to DB::connect() is true:
* + key The path to the key file.
* + cert The path to the certificate file.
* + ca The path to the certificate authority file.
* + capath The path to a directory that contains trusted SSL
* CA certificates in pem format.
* + cipher The list of allowable ciphers for SSL encryption.
*
* Example of how to connect using SSL:
* <code>
* require_once 'DB.php';
*
* $dsn = array(
* 'phptype' => 'mysqli',
* 'username' => 'someuser',
* 'password' => 'apasswd',
* 'hostspec' => 'localhost',
* 'database' => 'thedb',
* 'key' => 'client-key.pem',
* 'cert' => 'client-cert.pem',
* 'ca' => 'cacert.pem',
* 'capath' => '/path/to/ca/dir',
* 'cipher' => 'AES',
* );
*
* $options = array(
* 'ssl' => true,
* );
*
* $db = DB::connect($dsn, $options);
* if (PEAR::isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('mysqli')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
$ini = ini_get('track_errors');
@ini_set('track_errors', 1);
$php_errormsg = '';
 
if (((int) $this->getOption('ssl')) === 1) {
$init = mysqli_init();
mysqli_ssl_set(
$init,
empty($dsn['key']) ? null : $dsn['key'],
empty($dsn['cert']) ? null : $dsn['cert'],
empty($dsn['ca']) ? null : $dsn['ca'],
empty($dsn['capath']) ? null : $dsn['capath'],
empty($dsn['cipher']) ? null : $dsn['cipher']
);
if ($this->connection = @mysqli_real_connect(
$init,
$dsn['hostspec'],
$dsn['username'],
$dsn['password'],
$dsn['database'],
$dsn['port'],
$dsn['socket']))
{
$this->connection = $init;
}
} else {
$this->connection = @mysqli_connect(
$dsn['hostspec'],
$dsn['username'],
$dsn['password'],
$dsn['database'],
$dsn['port'],
$dsn['socket']
);
}
 
@ini_set('track_errors', $ini);
 
if (!$this->connection) {
if (($err = @mysqli_connect_error()) != '') {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$err);
} else {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$php_errormsg);
}
}
 
if ($dsn['database']) {
$this->_db = $dsn['database'];
}
 
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @mysqli_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$ismanip = $this->_checkManip($query);
$this->last_query = $query;
$query = $this->modifyQuery($query);
if ($this->_db) {
if (!@mysqli_select_db($this->connection, $this->_db)) {
return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
}
}
if (!$this->autocommit && $ismanip) {
if ($this->transaction_opcount == 0) {
$result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0');
$result = @mysqli_query($this->connection, 'BEGIN');
if (!$result) {
return $this->mysqliRaiseError();
}
}
$this->transaction_opcount++;
}
$result = @mysqli_query($this->connection, $query);
if (!$result) {
return $this->mysqliRaiseError();
}
if (is_object($result)) {
return $result;
}
return DB_OK;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal mysql result pointer to the next available result.
*
* This method has not been implemented yet.
*
* @param resource $result a valid sql result resource
* @return false
* @access public
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
if (!@mysqli_data_seek($result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @mysqli_fetch_array($result, MYSQLI_ASSOC);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @mysqli_fetch_row($result);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
/*
* Even though this DBMS already trims output, we do this because
* a field might have intentional whitespace at the end that
* gets removed by DB_PORTABILITY_RTRIM under another driver.
*/
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? mysqli_free_result($result) : false;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @mysqli_num_fields($result);
if (!$cols) {
return $this->mysqliRaiseError();
}
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$rows = @mysqli_num_rows($result);
if ($rows === null) {
return $this->mysqliRaiseError();
}
return $rows;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = false)
{
// XXX if $this->transaction_opcount > 0, we should probably
// issue a warning here.
$this->autocommit = $onoff ? true : false;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
if ($this->transaction_opcount > 0) {
if ($this->_db) {
if (!@mysqli_select_db($this->connection, $this->_db)) {
return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
}
}
$result = @mysqli_query($this->connection, 'COMMIT');
$result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
$this->transaction_opcount = 0;
if (!$result) {
return $this->mysqliRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
if ($this->transaction_opcount > 0) {
if ($this->_db) {
if (!@mysqli_select_db($this->connection, $this->_db)) {
return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
}
}
$result = @mysqli_query($this->connection, 'ROLLBACK');
$result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
$this->transaction_opcount = 0;
if (!$result) {
return $this->mysqliRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if ($this->_last_query_manip) {
return @mysqli_affected_rows($this->connection);
} else {
return 0;
}
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_mysqli::createSequence(), DB_mysqli::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
do {
$repeat = 0;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query('UPDATE ' . $seqname
. ' SET id = LAST_INSERT_ID(id + 1)');
$this->popErrorHandling();
if ($result === DB_OK) {
// COMMON CASE
$id = @mysqli_insert_id($this->connection);
if ($id != 0) {
return $id;
}
 
// EMPTY SEQ TABLE
// Sequence table must be empty for some reason,
// so fill it and return 1
// Obtain a user-level lock
$result = $this->getOne('SELECT GET_LOCK('
. "'${seqname}_lock', 10)");
if (DB::isError($result)) {
return $this->raiseError($result);
}
if ($result == 0) {
return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
}
 
// add the default value
$result = $this->query('REPLACE INTO ' . $seqname
. ' (id) VALUES (0)');
if (DB::isError($result)) {
return $this->raiseError($result);
}
 
// Release the lock
$result = $this->getOne('SELECT RELEASE_LOCK('
. "'${seqname}_lock')");
if (DB::isError($result)) {
return $this->raiseError($result);
}
// We know what the result will be, so no need to try again
return 1;
 
} elseif ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE)
{
// ONDEMAND TABLE CREATION
$result = $this->createSequence($seq_name);
 
// Since createSequence initializes the ID to be 1,
// we do not need to retrieve the ID again (or we will get 2)
if (DB::isError($result)) {
return $this->raiseError($result);
} else {
// First ID of a newly created sequence is 1
return 1;
}
 
} elseif (DB::isError($result) &&
$result->getCode() == DB_ERROR_ALREADY_EXISTS)
{
// BACKWARDS COMPAT
// see _BCsequence() comment
$result = $this->_BCsequence($seqname);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$repeat = 1;
}
} while ($repeat);
 
return $this->raiseError($result);
}
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_mysqli::nextID(), DB_mysqli::dropSequence()
*/
function createSequence($seq_name)
{
$seqname = $this->getSequenceName($seq_name);
$res = $this->query('CREATE TABLE ' . $seqname
. ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
. ' PRIMARY KEY(id))');
if (DB::isError($res)) {
return $res;
}
// insert yields value 1, nextId call will generate ID 2
return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_mysql::nextID(), DB_mysql::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ _BCsequence()
 
/**
* Backwards compatibility with old sequence emulation implementation
* (clean up the dupes)
*
* @param string $seqname the sequence name to clean up
*
* @return bool true on success. A DB_Error object on failure.
*
* @access private
*/
function _BCsequence($seqname)
{
// Obtain a user-level lock... this will release any previous
// application locks, but unlike LOCK TABLES, it does not abort
// the current transaction and is much less frequently used.
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
if (DB::isError($result)) {
return $result;
}
if ($result == 0) {
// Failed to get the lock, can't do the conversion, bail
// with a DB_ERROR_NOT_LOCKED error
return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
}
 
$highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
if (DB::isError($highest_id)) {
return $highest_id;
}
 
// This should kill all rows except the highest
// We should probably do something if $highest_id isn't
// numeric, but I'm at a loss as how to handle that...
$result = $this->query('DELETE FROM ' . $seqname
. " WHERE id <> $highest_id");
if (DB::isError($result)) {
return $result;
}
 
// If another thread has been waiting for this lock,
// it will go thru the above procedure, but will have no
// real effect
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
if (DB::isError($result)) {
return $result;
}
return true;
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* Quotes a string so it can be safely used as a table or column name
* (WARNING: using names that require this is a REALLY BAD IDEA)
*
* WARNING: Older versions of MySQL can't handle the backtick
* character (<kbd>`</kbd>) in table or column names.
*
* @param string $str identifier name to be quoted
*
* @return string quoted identifier string
*
* @see DB_common::quoteIdentifier()
* @since Method available since Release 1.6.0
*/
function quoteIdentifier($str)
{
return '`' . str_replace('`', '``', $str) . '`';
}
 
// }}}
// {{{ escapeSimple()
 
/**
* Escapes a string according to the current DBMS's standards
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @see DB_common::quoteSmart()
* @since Method available since Release 1.6.0
*/
function escapeSimple($str)
{
return @mysqli_real_escape_string($this->connection, $str);
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* Adds LIMIT clauses to a query string according to current DBMS standards
*
* @param string $query the query to modify
* @param int $from the row to start to fetching (0 = the first row)
* @param int $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return string the query string with LIMIT clauses added
*
* @access protected
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
if (DB::isManip($query) || $this->_next_query_manip) {
return $query . " LIMIT $count";
} else {
return $query . " LIMIT $from, $count";
}
}
 
// }}}
// {{{ mysqliRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_mysqli::errorNative(), DB_common::errorCode()
*/
function mysqliRaiseError($errno = null)
{
if ($errno === null) {
if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
$this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
$this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
} else {
// Doing this in case mode changes during runtime.
$this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
$this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
}
$errno = $this->errorCode(mysqli_errno($this->connection));
}
return $this->raiseError($errno, null, null, null,
@mysqli_errno($this->connection) . ' ** ' .
@mysqli_error($this->connection));
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error code produced by the last query
*
* @return int the DBMS' error code
*/
function errorNative()
{
return @mysqli_errno($this->connection);
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::setOption()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
// Fix for bug #11580.
if ($this->_db) {
if (!@mysqli_select_db($this->connection, $this->_db)) {
return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
}
}
 
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @mysqli_query($this->connection,
"SELECT * FROM $result LIMIT 0");
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_a($id, 'mysqli_result')) {
return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @mysqli_num_fields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$tmp = @mysqli_fetch_field($id);
 
$flags = '';
foreach ($this->mysqli_flags as $const => $means) {
if ($tmp->flags & $const) {
$flags .= $means . ' ';
}
}
if ($tmp->def) {
$flags .= 'default_' . rawurlencode($tmp->def);
}
$flags = trim($flags);
 
$res[$i] = array(
'table' => $case_func($tmp->table),
'name' => $case_func($tmp->name),
'type' => isset($this->mysqli_types[$tmp->type])
? $this->mysqli_types[$tmp->type]
: 'unknown',
// http://bugs.php.net/?id=36579
'len' => $tmp->length,
'flags' => $flags,
);
 
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@mysqli_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SHOW TABLES';
case 'users':
return 'SELECT DISTINCT User FROM mysql.user';
case 'databases':
return 'SHOW DATABASES';
default:
return null;
}
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/mssql.php
Новый файл
0,0 → 1,963
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's mssql extension
* for interacting with Microsoft SQL Server databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Sterling Hughes <sterling@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: mssql.php,v 1.92 2007/09/21 13:40:41 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's mssql extension
* for interacting with Microsoft SQL Server databases
*
* These methods overload the ones declared in DB_common.
*
* DB's mssql driver is only for Microsfoft SQL Server databases.
*
* If you're connecting to a Sybase database, you MUST specify "sybase"
* as the "phptype" in the DSN.
*
* This class only works correctly if you have compiled PHP using
* --with-mssql=[dir_to_FreeTDS].
*
* @category Database
* @package DB
* @author Sterling Hughes <sterling@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_mssql extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'mssql';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'mssql';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'emulate',
'new_link' => false,
'numrows' => true,
'pconnect' => true,
'prepare' => false,
'ssl' => false,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
// XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
var $errorcode_map = array(
102 => DB_ERROR_SYNTAX,
110 => DB_ERROR_VALUE_COUNT_ON_ROW,
155 => DB_ERROR_NOSUCHFIELD,
156 => DB_ERROR_SYNTAX,
170 => DB_ERROR_SYNTAX,
207 => DB_ERROR_NOSUCHFIELD,
208 => DB_ERROR_NOSUCHTABLE,
245 => DB_ERROR_INVALID_NUMBER,
319 => DB_ERROR_SYNTAX,
321 => DB_ERROR_NOSUCHFIELD,
325 => DB_ERROR_SYNTAX,
336 => DB_ERROR_SYNTAX,
515 => DB_ERROR_CONSTRAINT_NOT_NULL,
547 => DB_ERROR_CONSTRAINT,
1018 => DB_ERROR_SYNTAX,
1035 => DB_ERROR_SYNTAX,
1913 => DB_ERROR_ALREADY_EXISTS,
2209 => DB_ERROR_SYNTAX,
2223 => DB_ERROR_SYNTAX,
2248 => DB_ERROR_SYNTAX,
2256 => DB_ERROR_SYNTAX,
2257 => DB_ERROR_SYNTAX,
2627 => DB_ERROR_CONSTRAINT,
2714 => DB_ERROR_ALREADY_EXISTS,
3607 => DB_ERROR_DIVZERO,
3701 => DB_ERROR_NOSUCHTABLE,
7630 => DB_ERROR_SYNTAX,
8134 => DB_ERROR_DIVZERO,
9303 => DB_ERROR_SYNTAX,
9317 => DB_ERROR_SYNTAX,
9318 => DB_ERROR_SYNTAX,
9331 => DB_ERROR_SYNTAX,
9332 => DB_ERROR_SYNTAX,
15253 => DB_ERROR_SYNTAX,
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
var $autocommit = true;
 
/**
* The quantity of transactions begun
*
* {@internal While this is private, it can't actually be designated
* private in PHP 5 because it is directly accessed in the test suite.}}
*
* @var integer
* @access private
*/
var $transaction_opcount = 0;
 
/**
* The database specified in the DSN
*
* It's a fix to allow calls to different databases in the same script.
*
* @var string
* @access private
*/
var $_db = null;
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_mssql()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase')
&& !PEAR::loadExtension('sybase_ct'))
{
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
$params = array(
$dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
$dsn['username'] ? $dsn['username'] : null,
$dsn['password'] ? $dsn['password'] : null,
);
if ($dsn['port']) {
$params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':')
. $dsn['port'];
}
 
$connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
 
$this->connection = @call_user_func_array($connect_function, $params);
 
if (!$this->connection) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
@mssql_get_last_message());
}
if ($dsn['database']) {
if (!@mssql_select_db($dsn['database'], $this->connection)) {
return $this->raiseError(DB_ERROR_NODBSELECTED,
null, null, null,
@mssql_get_last_message());
}
$this->_db = $dsn['database'];
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @mssql_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$ismanip = $this->_checkManip($query);
$this->last_query = $query;
if (!@mssql_select_db($this->_db, $this->connection)) {
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
}
$query = $this->modifyQuery($query);
if (!$this->autocommit && $ismanip) {
if ($this->transaction_opcount == 0) {
$result = @mssql_query('BEGIN TRAN', $this->connection);
if (!$result) {
return $this->mssqlRaiseError();
}
}
$this->transaction_opcount++;
}
$result = @mssql_query($query, $this->connection);
if (!$result) {
return $this->mssqlRaiseError();
}
// Determine which queries that should return data, and which
// should return an error code only.
return $ismanip ? DB_OK : $result;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal mssql result pointer to the next available result
*
* @param a valid fbsql result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return @mssql_next_result($result);
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
if (!@mssql_data_seek($result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @mssql_fetch_assoc($result);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @mssql_fetch_row($result);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? mssql_free_result($result) : false;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @mssql_num_fields($result);
if (!$cols) {
return $this->mssqlRaiseError();
}
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$rows = @mssql_num_rows($result);
if ($rows === false) {
return $this->mssqlRaiseError();
}
return $rows;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = false)
{
// XXX if $this->transaction_opcount > 0, we should probably
// issue a warning here.
$this->autocommit = $onoff ? true : false;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
if ($this->transaction_opcount > 0) {
if (!@mssql_select_db($this->_db, $this->connection)) {
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
}
$result = @mssql_query('COMMIT TRAN', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->mssqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
if ($this->transaction_opcount > 0) {
if (!@mssql_select_db($this->_db, $this->connection)) {
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
}
$result = @mssql_query('ROLLBACK TRAN', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->mssqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if ($this->_last_query_manip) {
$res = @mssql_query('select @@rowcount', $this->connection);
if (!$res) {
return $this->mssqlRaiseError();
}
$ar = @mssql_fetch_row($res);
if (!$ar) {
$result = 0;
} else {
@mssql_free_result($res);
$result = $ar[0];
}
} else {
$result = 0;
}
return $result;
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_mssql::createSequence(), DB_mssql::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
if (!@mssql_select_db($this->_db, $this->connection)) {
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
}
$repeat = 0;
do {
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
$this->popErrorHandling();
if ($ondemand && DB::isError($result) &&
($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
{
$repeat = 1;
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $this->raiseError($result);
}
} elseif (!DB::isError($result)) {
$result = $this->query("SELECT IDENT_CURRENT('$seqname')");
if (DB::isError($result)) {
/* Fallback code for MS SQL Server 7.0, which doesn't have
* IDENT_CURRENT. This is *not* safe for concurrent
* requests, and really, if you're using it, you're in a
* world of hurt. Nevertheless, it's here to ensure BC. See
* bug #181 for the gory details.*/
$result = $this->query("SELECT @@IDENTITY FROM $seqname");
}
$repeat = 0;
} else {
$repeat = false;
}
} while ($repeat);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$result = $result->fetchRow(DB_FETCHMODE_ORDERED);
return $result[0];
}
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_mssql::nextID(), DB_mssql::dropSequence()
*/
function createSequence($seq_name)
{
return $this->query('CREATE TABLE '
. $this->getSequenceName($seq_name)
. ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
. ' [vapor] [int] NULL)');
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_mssql::nextID(), DB_mssql::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* Quotes a string so it can be safely used as a table or column name
*
* @param string $str identifier name to be quoted
*
* @return string quoted identifier string
*
* @see DB_common::quoteIdentifier()
* @since Method available since Release 1.6.0
*/
function quoteIdentifier($str)
{
return '[' . str_replace(']', ']]', $str) . ']';
}
 
// }}}
// {{{ mssqlRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_mssql::errorNative(), DB_mssql::errorCode()
*/
function mssqlRaiseError($code = null)
{
$message = @mssql_get_last_message();
if (!$code) {
$code = $this->errorNative();
}
return $this->raiseError($this->errorCode($code, $message),
null, null, null, "$code - $message");
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error code produced by the last query
*
* @return int the DBMS' error code
*/
function errorNative()
{
$res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
if (!$res) {
return DB_ERROR;
}
$row = @mssql_fetch_row($res);
return $row[0];
}
 
// }}}
// {{{ errorCode()
 
/**
* Determines PEAR::DB error code from mssql's native codes.
*
* If <var>$nativecode</var> isn't known yet, it will be looked up.
*
* @param mixed $nativecode mssql error code, if known
* @return integer an error number from a DB error constant
* @see errorNative()
*/
function errorCode($nativecode = null, $msg = '')
{
if (!$nativecode) {
$nativecode = $this->errorNative();
}
if (isset($this->errorcode_map[$nativecode])) {
if ($nativecode == 3701
&& preg_match('/Cannot drop the index/i', $msg))
{
return DB_ERROR_NOT_FOUND;
}
return $this->errorcode_map[$nativecode];
} else {
return DB_ERROR;
}
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* NOTE: only supports 'table' and 'flags' if <var>$result</var>
* is a table name.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
if (!@mssql_select_db($this->_db, $this->connection)) {
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
}
$id = @mssql_query("SELECT * FROM $result WHERE 1=0",
$this->connection);
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @mssql_num_fields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
if ($got_string) {
$flags = $this->_mssql_field_flags($result,
@mssql_field_name($id, $i));
if (DB::isError($flags)) {
return $flags;
}
} else {
$flags = '';
}
 
$res[$i] = array(
'table' => $got_string ? $case_func($result) : '',
'name' => $case_func(@mssql_field_name($id, $i)),
'type' => @mssql_field_type($id, $i),
'len' => @mssql_field_length($id, $i),
'flags' => $flags,
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@mssql_free_result($id);
}
return $res;
}
 
// }}}
// {{{ _mssql_field_flags()
 
/**
* Get a column's flags
*
* Supports "not_null", "primary_key",
* "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
* "unique_key" (mssql unique index, unique check or primary_key) and
* "multiple_key" (multikey index)
*
* mssql timestamp is NOT similar to the mysql timestamp so this is maybe
* not useful at all - is the behaviour of mysql_field_flags that primary
* keys are alway unique? is the interpretation of multiple_key correct?
*
* @param string $table the table name
* @param string $column the field name
*
* @return string the flags
*
* @access private
* @author Joern Barthel <j_barthel@web.de>
*/
function _mssql_field_flags($table, $column)
{
static $tableName = null;
static $flags = array();
 
if ($table != $tableName) {
 
$flags = array();
$tableName = $table;
 
// get unique and primary keys
$res = $this->getAll("EXEC SP_HELPINDEX $table", DB_FETCHMODE_ASSOC);
if (DB::isError($res)) {
return $res;
}
 
foreach ($res as $val) {
$keys = explode(', ', $val['index_keys']);
 
if (sizeof($keys) > 1) {
foreach ($keys as $key) {
$this->_add_flag($flags[$key], 'multiple_key');
}
}
 
if (strpos($val['index_description'], 'primary key')) {
foreach ($keys as $key) {
$this->_add_flag($flags[$key], 'primary_key');
}
} elseif (strpos($val['index_description'], 'unique')) {
foreach ($keys as $key) {
$this->_add_flag($flags[$key], 'unique_key');
}
}
}
 
// get auto_increment, not_null and timestamp
$res = $this->getAll("EXEC SP_COLUMNS $table", DB_FETCHMODE_ASSOC);
if (DB::isError($res)) {
return $res;
}
 
foreach ($res as $val) {
$val = array_change_key_case($val, CASE_LOWER);
if ($val['nullable'] == '0') {
$this->_add_flag($flags[$val['column_name']], 'not_null');
}
if (strpos($val['type_name'], 'identity')) {
$this->_add_flag($flags[$val['column_name']], 'auto_increment');
}
if (strpos($val['type_name'], 'timestamp')) {
$this->_add_flag($flags[$val['column_name']], 'timestamp');
}
}
}
 
if (array_key_exists($column, $flags)) {
return(implode(' ', $flags[$column]));
}
return '';
}
 
// }}}
// {{{ _add_flag()
 
/**
* Adds a string to the flags array if the flag is not yet in there
* - if there is no flag present the array is created
*
* @param array &$array the reference to the flag-array
* @param string $value the flag value
*
* @return void
*
* @access private
* @author Joern Barthel <j_barthel@web.de>
*/
function _add_flag(&$array, $value)
{
if (!is_array($array)) {
$array = array($value);
} elseif (!in_array($value, $array)) {
array_push($array, $value);
}
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return "SELECT name FROM sysobjects WHERE type = 'U'"
. ' ORDER BY name';
case 'views':
return "SELECT name FROM sysobjects WHERE type = 'V'";
default:
return null;
}
}
 
// }}}
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/sqlite.php
Новый файл
0,0 → 1,959
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's sqlite extension
* for interacting with SQLite databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Urs Gehrig <urs@circle.ch>
* @author Mika Tuupola <tuupola@appelsiini.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
* @version CVS: $Id: sqlite.php,v 1.117 2007/09/21 14:23:28 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's sqlite extension
* for interacting with SQLite databases
*
* These methods overload the ones declared in DB_common.
*
* NOTICE: This driver needs PHP's track_errors ini setting to be on.
* It is automatically turned on when connecting to the database.
* Make sure your scripts don't turn it off.
*
* @category Database
* @package DB
* @author Urs Gehrig <urs@circle.ch>
* @author Mika Tuupola <tuupola@appelsiini.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_sqlite extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'sqlite';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'sqlite';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'alter',
'new_link' => false,
'numrows' => true,
'pconnect' => true,
'prepare' => false,
'ssl' => false,
'transactions' => false,
);
 
/**
* A mapping of native error codes to DB error codes
*
* {@internal Error codes according to sqlite_exec. See the online
* manual at http://sqlite.org/c_interface.html for info.
* This error handling based on sqlite_exec is not yet implemented.}}
*
* @var array
*/
var $errorcode_map = array(
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* SQLite data types
*
* @link http://www.sqlite.org/datatypes.html
*
* @var array
*/
var $keywords = array (
'BLOB' => '',
'BOOLEAN' => '',
'CHARACTER' => '',
'CLOB' => '',
'FLOAT' => '',
'INTEGER' => '',
'KEY' => '',
'NATIONAL' => '',
'NUMERIC' => '',
'NVARCHAR' => '',
'PRIMARY' => '',
'TEXT' => '',
'TIMESTAMP' => '',
'UNIQUE' => '',
'VARCHAR' => '',
'VARYING' => '',
);
 
/**
* The most recent error message from $php_errormsg
* @var string
* @access private
*/
var $_lasterror = '';
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_sqlite()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* PEAR DB's sqlite driver supports the following extra DSN options:
* + mode The permissions for the database file, in four digit
* chmod octal format (eg "0600").
*
* Example of connecting to a database in read-only mode:
* <code>
* require_once 'DB.php';
*
* $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400';
* $options = array(
* 'portability' => DB_PORTABILITY_ALL,
* );
*
* $db = DB::connect($dsn, $options);
* if (PEAR::isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('sqlite')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
if (!$dsn['database']) {
return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
}
 
if ($dsn['database'] !== ':memory:') {
if (!file_exists($dsn['database'])) {
if (!touch($dsn['database'])) {
return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
}
if (!isset($dsn['mode']) ||
!is_numeric($dsn['mode']))
{
$mode = 0644;
} else {
$mode = octdec($dsn['mode']);
}
if (!chmod($dsn['database'], $mode)) {
return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
}
if (!file_exists($dsn['database'])) {
return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
}
}
if (!is_file($dsn['database'])) {
return $this->sqliteRaiseError(DB_ERROR_INVALID);
}
if (!is_readable($dsn['database'])) {
return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
}
}
 
$connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open';
 
// track_errors must remain on for simpleQuery()
@ini_set('track_errors', 1);
$php_errormsg = '';
 
if (!$this->connection = @$connect_function($dsn['database'])) {
return $this->raiseError(DB_ERROR_NODBSELECTED,
null, null, null,
$php_errormsg);
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @sqlite_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* NOTICE: This method needs PHP's track_errors ini setting to be on.
* It is automatically turned on when connecting to the database.
* Make sure your scripts don't turn it off.
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$ismanip = $this->_checkManip($query);
$this->last_query = $query;
$query = $this->modifyQuery($query);
 
$php_errormsg = '';
 
$result = @sqlite_query($query, $this->connection);
$this->_lasterror = $php_errormsg ? $php_errormsg : '';
 
$this->result = $result;
if (!$this->result) {
return $this->sqliteRaiseError(null);
}
 
// sqlite_query() seems to allways return a resource
// so cant use that. Using $ismanip instead
if (!$ismanip) {
$numRows = $this->numRows($result);
if (is_object($numRows)) {
// we've got PEAR_Error
return $numRows;
}
return $result;
}
return DB_OK;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal sqlite result pointer to the next available result
*
* @param resource $result the valid sqlite result resource
*
* @return bool true if a result is available otherwise return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
if (!@sqlite_seek($this->result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @sqlite_fetch_array($result, SQLITE_ASSOC);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
 
/* Remove extraneous " characters from the fields in the result.
* Fixes bug #11716. */
if (is_array($arr) && count($arr) > 0) {
$strippedArr = array();
foreach ($arr as $field => $value) {
$strippedArr[trim($field, '"')] = $value;
}
$arr = $strippedArr;
}
} else {
$arr = @sqlite_fetch_array($result, SQLITE_NUM);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
/*
* Even though this DBMS already trims output, we do this because
* a field might have intentional whitespace at the end that
* gets removed by DB_PORTABILITY_RTRIM under another driver.
*/
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult(&$result)
{
// XXX No native free?
if (!is_resource($result)) {
return false;
}
$result = null;
return true;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @sqlite_num_fields($result);
if (!$cols) {
return $this->sqliteRaiseError();
}
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$rows = @sqlite_num_rows($result);
if ($rows === null) {
return $this->sqliteRaiseError();
}
return $rows;
}
 
// }}}
// {{{ affected()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
return @sqlite_changes($this->connection);
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_sqlite::nextID(), DB_sqlite::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_sqlite::nextID(), DB_sqlite::dropSequence()
*/
function createSequence($seq_name)
{
$seqname = $this->getSequenceName($seq_name);
$query = 'CREATE TABLE ' . $seqname .
' (id INTEGER UNSIGNED PRIMARY KEY) ';
$result = $this->query($query);
if (DB::isError($result)) {
return($result);
}
$query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname
BEGIN
DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID();
END ";
$result = $this->query($query);
if (DB::isError($result)) {
return($result);
}
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_sqlite::createSequence(), DB_sqlite::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
 
do {
$repeat = 0;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)");
$this->popErrorHandling();
if ($result === DB_OK) {
$id = @sqlite_last_insert_rowid($this->connection);
if ($id != 0) {
return $id;
}
} elseif ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE)
{
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $this->raiseError($result);
} else {
$repeat = 1;
}
}
} while ($repeat);
 
return $this->raiseError($result);
}
 
// }}}
// {{{ getDbFileStats()
 
/**
* Get the file stats for the current database
*
* Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size,
* atime, mtime, ctime, blksize, blocks or a numeric key between
* 0 and 12.
*
* @param string $arg the array key for stats()
*
* @return mixed an array on an unspecified key, integer on a passed
* arg and false at a stats error
*/
function getDbFileStats($arg = '')
{
$stats = stat($this->dsn['database']);
if ($stats == false) {
return false;
}
if (is_array($stats)) {
if (is_numeric($arg)) {
if (((int)$arg <= 12) & ((int)$arg >= 0)) {
return false;
}
return $stats[$arg ];
}
if (array_key_exists(trim($arg), $stats)) {
return $stats[$arg ];
}
}
return $stats;
}
 
// }}}
// {{{ escapeSimple()
 
/**
* Escapes a string according to the current DBMS's standards
*
* In SQLite, this makes things safe for inserts/updates, but may
* cause problems when performing text comparisons against columns
* containing binary data. See the
* {@link http://php.net/sqlite_escape_string PHP manual} for more info.
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @since Method available since Release 1.6.1
* @see DB_common::escapeSimple()
*/
function escapeSimple($str)
{
return @sqlite_escape_string($str);
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* Adds LIMIT clauses to a query string according to current DBMS standards
*
* @param string $query the query to modify
* @param int $from the row to start to fetching (0 = the first row)
* @param int $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return string the query string with LIMIT clauses added
*
* @access protected
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
return "$query LIMIT $count OFFSET $from";
}
 
// }}}
// {{{ modifyQuery()
 
/**
* Changes a query string for various DBMS specific reasons
*
* This little hack lets you know how many rows were deleted
* when running a "DELETE FROM table" query. Only implemented
* if the DB_PORTABILITY_DELETE_COUNT portability option is on.
*
* @param string $query the query string to modify
*
* @return string the modified query string
*
* @access protected
* @see DB_common::setOption()
*/
function modifyQuery($query)
{
if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
$query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
'DELETE FROM \1 WHERE 1=1', $query);
}
}
return $query;
}
 
// }}}
// {{{ sqliteRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_sqlite::errorNative(), DB_sqlite::errorCode()
*/
function sqliteRaiseError($errno = null)
{
$native = $this->errorNative();
if ($errno === null) {
$errno = $this->errorCode($native);
}
 
$errorcode = @sqlite_last_error($this->connection);
$userinfo = "$errorcode ** $this->last_query";
 
return $this->raiseError($errno, null, null, $userinfo, $native);
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error message produced by the last query
*
* {@internal This is used to retrieve more meaningfull error messages
* because sqlite_last_error() does not provide adequate info.}}
*
* @return string the DBMS' error message
*/
function errorNative()
{
return $this->_lasterror;
}
 
// }}}
// {{{ errorCode()
 
/**
* Determines PEAR::DB error code from the database's text error message
*
* @param string $errormsg the error message returned from the database
*
* @return integer the DB error number
*/
function errorCode($errormsg)
{
static $error_regexps;
// PHP 5.2+ prepends the function name to $php_errormsg, so we need
// this hack to work around it, per bug #9599.
$errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg);
if (!isset($error_regexps)) {
$error_regexps = array(
'/^no such table:/' => DB_ERROR_NOSUCHTABLE,
'/^no such index:/' => DB_ERROR_NOT_FOUND,
'/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS,
'/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT,
'/is not unique/' => DB_ERROR_CONSTRAINT,
'/columns .* are not unique/i' => DB_ERROR_CONSTRAINT,
'/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT,
'/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL,
'/^no such column:/' => DB_ERROR_NOSUCHFIELD,
'/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD,
'/^near ".*": syntax error$/' => DB_ERROR_SYNTAX,
'/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW,
);
}
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $errormsg)) {
return $code;
}
}
// Fall back to DB_ERROR if there was no mapping.
return DB_ERROR;
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table
*
* @param string $result a string containing the name of a table
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.7.0
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @sqlite_array_query($this->connection,
"PRAGMA table_info('$result');",
SQLITE_ASSOC);
$got_string = true;
} else {
$this->last_query = '';
return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null,
'This DBMS can not obtain tableInfo' .
' from result sets');
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = count($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
if (strpos($id[$i]['type'], '(') !== false) {
$bits = explode('(', $id[$i]['type']);
$type = $bits[0];
$len = rtrim($bits[1],')');
} else {
$type = $id[$i]['type'];
$len = 0;
}
 
$flags = '';
if ($id[$i]['pk']) {
$flags .= 'primary_key ';
}
if ($id[$i]['notnull']) {
$flags .= 'not_null ';
}
if ($id[$i]['dflt_value'] !== null) {
$flags .= 'default_' . rawurlencode($id[$i]['dflt_value']);
}
$flags = trim($flags);
 
$res[$i] = array(
'table' => $case_func($result),
'name' => $case_func($id[$i]['name']),
'type' => $type,
'len' => $len,
'flags' => $flags,
);
 
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
* @param array $args SQLITE DRIVER ONLY: a private array of arguments
* used by the getSpecialQuery(). Do not use
* this directly.
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type, $args = array())
{
if (!is_array($args)) {
return $this->raiseError('no key specified', null, null, null,
'Argument has to be an array.');
}
 
switch ($type) {
case 'master':
return 'SELECT * FROM sqlite_master;';
case 'tables':
return "SELECT name FROM sqlite_master WHERE type='table' "
. 'UNION ALL SELECT name FROM sqlite_temp_master '
. "WHERE type='table' ORDER BY name;";
case 'schema':
return 'SELECT sql FROM (SELECT * FROM sqlite_master '
. 'UNION ALL SELECT * FROM sqlite_temp_master) '
. "WHERE type!='meta' "
. 'ORDER BY tbl_name, type DESC, name;';
case 'schemax':
case 'schema_x':
/*
* Use like:
* $res = $db->query($db->getSpecialQuery('schema_x',
* array('table' => 'table3')));
*/
return 'SELECT sql FROM (SELECT * FROM sqlite_master '
. 'UNION ALL SELECT * FROM sqlite_temp_master) '
. "WHERE tbl_name LIKE '{$args['table']}' "
. "AND type!='meta' "
. 'ORDER BY type DESC, name;';
case 'alter':
/*
* SQLite does not support ALTER TABLE; this is a helper query
* to handle this. 'table' represents the table name, 'rows'
* the news rows to create, 'save' the row(s) to keep _with_
* the data.
*
* Use like:
* $args = array(
* 'table' => $table,
* 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT",
* 'save' => "NULL, titel, content, datetime"
* );
* $res = $db->query( $db->getSpecialQuery('alter', $args));
*/
$rows = strtr($args['rows'], $this->keywords);
 
$q = array(
'BEGIN TRANSACTION',
"CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})",
"INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}",
"DROP TABLE {$args['table']}",
"CREATE TABLE {$args['table']} ({$args['rows']})",
"INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup",
"DROP TABLE {$args['table']}_backup",
'COMMIT',
);
 
/*
* This is a dirty hack, since the above query will not get
* executed with a single query call so here the query method
* will be called directly and return a select instead.
*/
foreach ($q as $query) {
$this->query($query);
}
return "SELECT * FROM {$args['table']};";
default:
return null;
}
}
 
// }}}
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/oci8.php
Новый файл
0,0 → 1,1156
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's oci8 extension
* for interacting with Oracle databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author James L. Pine <jlp@valinux.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: oci8.php,v 1.115 2007/09/21 13:40:41 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's oci8 extension
* for interacting with Oracle databases
*
* Definitely works with versions 8 and 9 of Oracle.
*
* These methods overload the ones declared in DB_common.
*
* Be aware... OCIError() only appears to return anything when given a
* statement, so functions return the generic DB_ERROR instead of more
* useful errors that have to do with feedback from the database.
*
* @category Database
* @package DB
* @author James L. Pine <jlp@valinux.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_oci8 extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'oci8';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'oci8';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'alter',
'new_link' => '5.0.0',
'numrows' => 'subquery',
'pconnect' => true,
'prepare' => true,
'ssl' => false,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
1 => DB_ERROR_CONSTRAINT,
900 => DB_ERROR_SYNTAX,
904 => DB_ERROR_NOSUCHFIELD,
913 => DB_ERROR_VALUE_COUNT_ON_ROW,
921 => DB_ERROR_SYNTAX,
923 => DB_ERROR_SYNTAX,
942 => DB_ERROR_NOSUCHTABLE,
955 => DB_ERROR_ALREADY_EXISTS,
1400 => DB_ERROR_CONSTRAINT_NOT_NULL,
1401 => DB_ERROR_INVALID,
1407 => DB_ERROR_CONSTRAINT_NOT_NULL,
1418 => DB_ERROR_NOT_FOUND,
1476 => DB_ERROR_DIVZERO,
1722 => DB_ERROR_INVALID_NUMBER,
2289 => DB_ERROR_NOSUCHTABLE,
2291 => DB_ERROR_CONSTRAINT,
2292 => DB_ERROR_CONSTRAINT,
2449 => DB_ERROR_CONSTRAINT,
12899 => DB_ERROR_INVALID,
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
var $autocommit = true;
 
/**
* Stores the $data passed to execute() in the oci8 driver
*
* Gets reset to array() when simpleQuery() is run.
*
* Needed in case user wants to call numRows() after prepare/execute
* was used.
*
* @var array
* @access private
*/
var $_data = array();
 
/**
* The result or statement handle from the most recently executed query
* @var resource
*/
var $last_stmt;
 
/**
* Is the given prepared statement a data manipulation query?
* @var array
* @access private
*/
var $manip_query = array();
 
/**
* Store of prepared SQL queries.
* @var array
* @access private
*/
var $_prepared_queries = array();
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_oci8()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* If PHP is at version 5.0.0 or greater:
* + Generally, oci_connect() or oci_pconnect() are used.
* + But if the new_link DSN option is set to true, oci_new_connect()
* is used.
*
* When using PHP version 4.x, OCILogon() or OCIPLogon() are used.
*
* PEAR DB's oci8 driver supports the following extra DSN options:
* + charset The character set to be used on the connection.
* Only used if PHP is at version 5.0.0 or greater
* and the Oracle server is at 9.2 or greater.
* Available since PEAR DB 1.7.0.
* + new_link If set to true, causes subsequent calls to
* connect() to return a new connection link
* instead of the existing one. WARNING: this is
* not portable to other DBMS's.
* Available since PEAR DB 1.7.0.
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('oci8')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
// Backwards compatibility with DB < 1.7.0
if (empty($dsn['database']) && !empty($dsn['hostspec'])) {
$db = $dsn['hostspec'];
} else {
$db = $dsn['database'];
}
 
if (function_exists('oci_connect')) {
if (isset($dsn['new_link'])
&& ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
{
$connect_function = 'oci_new_connect';
} else {
$connect_function = $persistent ? 'oci_pconnect'
: 'oci_connect';
}
if (isset($this->dsn['port']) && $this->dsn['port']) {
$db = '//'.$db.':'.$this->dsn['port'];
}
 
$char = empty($dsn['charset']) ? null : $dsn['charset'];
$this->connection = @$connect_function($dsn['username'],
$dsn['password'],
$db,
$char);
$error = OCIError();
if (!empty($error) && $error['code'] == 12541) {
// Couldn't find TNS listener. Try direct connection.
$this->connection = @$connect_function($dsn['username'],
$dsn['password'],
null,
$char);
}
} else {
$connect_function = $persistent ? 'OCIPLogon' : 'OCILogon';
if ($db) {
$this->connection = @$connect_function($dsn['username'],
$dsn['password'],
$db);
} elseif ($dsn['username'] || $dsn['password']) {
$this->connection = @$connect_function($dsn['username'],
$dsn['password']);
}
}
 
if (!$this->connection) {
$error = OCIError();
$error = (is_array($error)) ? $error['message'] : null;
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$error);
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
if (function_exists('oci_close')) {
$ret = @oci_close($this->connection);
} else {
$ret = @OCILogOff($this->connection);
}
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* To determine how many rows of a result set get buffered using
* ocisetprefetch(), see the "result_buffering" option in setOptions().
* This option was added in Release 1.7.0.
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$this->_data = array();
$this->last_parameters = array();
$this->last_query = $query;
$query = $this->modifyQuery($query);
$result = @OCIParse($this->connection, $query);
if (!$result) {
return $this->oci8RaiseError();
}
if ($this->autocommit) {
$success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS);
} else {
$success = @OCIExecute($result,OCI_DEFAULT);
}
if (!$success) {
return $this->oci8RaiseError($result);
}
$this->last_stmt = $result;
if ($this->_checkManip($query)) {
return DB_OK;
} else {
@ocisetprefetch($result, $this->options['result_buffering']);
return $result;
}
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal oracle result pointer to the next available result
*
* @param a valid oci8 result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE &&
$moredata)
{
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS);
}
if (!$moredata) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? OCIFreeStatement($result) : false;
}
 
/**
* Frees the internal resources associated with a prepared query
*
* @param resource $stmt the prepared statement's resource
* @param bool $free_resource should the PHP resource be freed too?
* Use false if you need to get data
* from the result set later.
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_oci8::prepare()
*/
function freePrepared($stmt, $free_resource = true)
{
if (!is_resource($stmt)) {
return false;
}
if ($free_resource) {
@ocifreestatement($stmt);
}
if (isset($this->prepare_types[(int)$stmt])) {
unset($this->prepare_types[(int)$stmt]);
unset($this->manip_query[(int)$stmt]);
} else {
return false;
}
return true;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* Only works if the DB_PORTABILITY_NUMROWS portability option
* is turned on.
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows(), DB_common::setOption()
*/
function numRows($result)
{
// emulate numRows for Oracle. yuck.
if ($this->options['portability'] & DB_PORTABILITY_NUMROWS &&
$result === $this->last_stmt)
{
$countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')';
$save_query = $this->last_query;
$save_stmt = $this->last_stmt;
 
$count = $this->query($countquery);
 
// Restore the last query and statement.
$this->last_query = $save_query;
$this->last_stmt = $save_stmt;
if (DB::isError($count) ||
DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED)))
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
return $row[0];
}
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @OCINumCols($result);
if (!$cols) {
return $this->oci8RaiseError($result);
}
return $cols;
}
 
// }}}
// {{{ prepare()
 
/**
* Prepares a query for multiple execution with execute().
*
* With oci8, this is emulated.
*
* prepare() requires a generic query as string like <code>
* INSERT INTO numbers VALUES (?, ?, ?)
* </code>. The <kbd>?</kbd> characters are placeholders.
*
* Three types of placeholders can be used:
* + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
* + <kbd>!</kbd> value is inserted 'as is'
* + <kbd>&</kbd> requires a file name. The file's contents get
* inserted into the query (i.e. saving binary
* data in a db)
*
* Use backslashes to escape placeholder characters if you don't want
* them to be interpreted as placeholders. Example: <code>
* "UPDATE foo SET col=? WHERE col='over \& under'"
* </code>
*
* @param string $query the query to be prepared
*
* @return mixed DB statement resource on success. DB_Error on failure.
*
* @see DB_oci8::execute()
*/
function prepare($query)
{
$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
PREG_SPLIT_DELIM_CAPTURE);
$binds = count($tokens) - 1;
$token = 0;
$types = array();
$newquery = '';
 
foreach ($tokens as $key => $val) {
switch ($val) {
case '?':
$types[$token++] = DB_PARAM_SCALAR;
unset($tokens[$key]);
break;
case '&':
$types[$token++] = DB_PARAM_OPAQUE;
unset($tokens[$key]);
break;
case '!':
$types[$token++] = DB_PARAM_MISC;
unset($tokens[$key]);
break;
default:
$tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
if ($key != $binds) {
$newquery .= $tokens[$key] . ':bind' . $token;
} else {
$newquery .= $tokens[$key];
}
}
}
 
$this->last_query = $query;
$newquery = $this->modifyQuery($newquery);
if (!$stmt = @OCIParse($this->connection, $newquery)) {
return $this->oci8RaiseError();
}
$this->prepare_types[(int)$stmt] = $types;
$this->manip_query[(int)$stmt] = DB::isManip($query);
$this->_prepared_queries[(int)$stmt] = $newquery;
return $stmt;
}
 
// }}}
// {{{ execute()
 
/**
* Executes a DB statement prepared with prepare().
*
* To determine how many rows of a result set get buffered using
* ocisetprefetch(), see the "result_buffering" option in setOptions().
* This option was added in Release 1.7.0.
*
* @param resource $stmt a DB statement resource returned from prepare()
* @param mixed $data array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 for non-array items or the
* quantity of elements in the array.
*
* @return mixed returns an oic8 result resource for successful SELECT
* queries, DB_OK for other successful queries.
* A DB error object is returned on failure.
*
* @see DB_oci8::prepare()
*/
function &execute($stmt, $data = array())
{
$data = (array)$data;
$this->last_parameters = $data;
$this->last_query = $this->_prepared_queries[(int)$stmt];
$this->_data = $data;
 
$types = $this->prepare_types[(int)$stmt];
if (count($types) != count($data)) {
$tmp = $this->raiseError(DB_ERROR_MISMATCH);
return $tmp;
}
 
$i = 0;
foreach ($data as $key => $value) {
if ($types[$i] == DB_PARAM_MISC) {
/*
* Oracle doesn't seem to have the ability to pass a
* parameter along unchanged, so strip off quotes from start
* and end, plus turn two single quotes to one single quote,
* in order to avoid the quotes getting escaped by
* Oracle and ending up in the database.
*/
$data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
$data[$key] = str_replace("''", "'", $data[$key]);
} elseif ($types[$i] == DB_PARAM_OPAQUE) {
$fp = @fopen($data[$key], 'rb');
if (!$fp) {
$tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
return $tmp;
}
$data[$key] = fread($fp, filesize($data[$key]));
fclose($fp);
} elseif ($types[$i] == DB_PARAM_SCALAR) {
// Floats have to be converted to a locale-neutral
// representation.
if (is_float($data[$key])) {
$data[$key] = $this->quoteFloat($data[$key]);
}
}
if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) {
$tmp = $this->oci8RaiseError($stmt);
return $tmp;
}
$this->last_query = str_replace(':bind'.$i, $this->quoteSmart($data[$key]), $this->last_query);
$i++;
}
if ($this->autocommit) {
$success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
} else {
$success = @OCIExecute($stmt, OCI_DEFAULT);
}
if (!$success) {
$tmp = $this->oci8RaiseError($stmt);
return $tmp;
}
$this->last_stmt = $stmt;
if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
$this->_last_query_manip = true;
$this->_next_query_manip = false;
$tmp = DB_OK;
} else {
$this->_last_query_manip = false;
@ocisetprefetch($stmt, $this->options['result_buffering']);
$tmp = new DB_result($this, $stmt);
}
return $tmp;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = false)
{
$this->autocommit = (bool)$onoff;;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
$result = @OCICommit($this->connection);
if (!$result) {
return $this->oci8RaiseError();
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
$result = @OCIRollback($this->connection);
if (!$result) {
return $this->oci8RaiseError();
}
return DB_OK;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if ($this->last_stmt === false) {
return $this->oci8RaiseError();
}
$result = @OCIRowCount($this->last_stmt);
if ($result === false) {
return $this->oci8RaiseError($this->last_stmt);
}
return $result;
}
 
// }}}
// {{{ modifyQuery()
 
/**
* Changes a query string for various DBMS specific reasons
*
* "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle.
*
* @param string $query the query string to modify
*
* @return string the modified query string
*
* @access protected
*/
function modifyQuery($query)
{
if (preg_match('/^\s*SELECT/i', $query) &&
!preg_match('/\sFROM\s/i', $query)) {
$query .= ' FROM dual';
}
return $query;
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* Adds LIMIT clauses to a query string according to current DBMS standards
*
* @param string $query the query to modify
* @param int $from the row to start to fetching (0 = the first row)
* @param int $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return string the query string with LIMIT clauses added
*
* @access protected
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
// Let Oracle return the name of the columns instead of
// coding a "home" SQL parser
 
if (count($params)) {
$result = $this->prepare("SELECT * FROM ($query) "
. 'WHERE NULL = NULL');
$tmp = $this->execute($result, $params);
} else {
$q_fields = "SELECT * FROM ($query) WHERE NULL = NULL";
 
if (!$result = @OCIParse($this->connection, $q_fields)) {
$this->last_query = $q_fields;
return $this->oci8RaiseError();
}
if (!@OCIExecute($result, OCI_DEFAULT)) {
$this->last_query = $q_fields;
return $this->oci8RaiseError($result);
}
}
 
$ncols = OCINumCols($result);
$cols = array();
for ( $i = 1; $i <= $ncols; $i++ ) {
$cols[] = '"' . OCIColumnName($result, $i) . '"';
}
$fields = implode(', ', $cols);
// XXX Test that (tip by John Lim)
//if (preg_match('/^\s*SELECT\s+/is', $query, $match)) {
// // Introduce the FIRST_ROWS Oracle query optimizer
// $query = substr($query, strlen($match[0]), strlen($query));
// $query = "SELECT /* +FIRST_ROWS */ " . $query;
//}
 
// Construct the query
// more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2
// Perhaps this could be optimized with the use of Unions
$query = "SELECT $fields FROM".
" (SELECT rownum as linenum, $fields FROM".
" ($query)".
' WHERE rownum <= '. ($from + $count) .
') WHERE linenum >= ' . ++$from;
return $query;
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_oci8::createSequence(), DB_oci8::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
$repeat = 0;
do {
$this->expectError(DB_ERROR_NOSUCHTABLE);
$result = $this->query("SELECT ${seqname}.nextval FROM dual");
$this->popExpect();
if ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE) {
$repeat = 1;
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $this->raiseError($result);
}
} else {
$repeat = 0;
}
} while ($repeat);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
return $arr[0];
}
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_oci8::nextID(), DB_oci8::dropSequence()
*/
function createSequence($seq_name)
{
return $this->query('CREATE SEQUENCE '
. $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_oci8::nextID(), DB_oci8::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP SEQUENCE '
. $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ oci8RaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_oci8::errorNative(), DB_oci8::errorCode()
*/
function oci8RaiseError($errno = null)
{
if ($errno === null) {
$error = @OCIError($this->connection);
return $this->raiseError($this->errorCode($error['code']),
null, null, null, $error['message']);
} elseif (is_resource($errno)) {
$error = @OCIError($errno);
return $this->raiseError($this->errorCode($error['code']),
null, null, null, $error['message']);
}
return $this->raiseError($this->errorCode($errno));
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error code produced by the last query
*
* @return int the DBMS' error code. FALSE if the code could not be
* determined
*/
function errorNative()
{
if (is_resource($this->last_stmt)) {
$error = @OCIError($this->last_stmt);
} else {
$error = @OCIError($this->connection);
}
if (is_array($error)) {
return $error['code'];
}
return false;
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* NOTE: only supports 'table' and 'flags' if <var>$result</var>
* is a table name.
*
* NOTE: flags won't contain index information.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$res = array();
 
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$result = strtoupper($result);
$q_fields = 'SELECT column_name, data_type, data_length, '
. 'nullable '
. 'FROM user_tab_columns '
. "WHERE table_name='$result' ORDER BY column_id";
 
$this->last_query = $q_fields;
 
if (!$stmt = @OCIParse($this->connection, $q_fields)) {
return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA);
}
if (!@OCIExecute($stmt, OCI_DEFAULT)) {
return $this->oci8RaiseError($stmt);
}
$i = 0;
while (@OCIFetch($stmt)) {
$res[$i] = array(
'table' => $case_func($result),
'name' => $case_func(@OCIResult($stmt, 1)),
'type' => @OCIResult($stmt, 2),
'len' => @OCIResult($stmt, 3),
'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '',
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
$i++;
}
 
if ($mode) {
$res['num_fields'] = $i;
}
@OCIFreeStatement($stmt);
 
} else {
if (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$result = $result->result;
}
 
$res = array();
 
if ($result === $this->last_stmt) {
$count = @OCINumCols($result);
if ($mode) {
$res['num_fields'] = $count;
}
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => '',
'name' => $case_func(@OCIColumnName($result, $i+1)),
'type' => @OCIColumnType($result, $i+1),
'len' => @OCIColumnSize($result, $i+1),
'flags' => '',
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
} else {
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SELECT table_name FROM user_tables';
case 'synonyms':
return 'SELECT synonym_name FROM user_synonyms';
case 'views':
return 'SELECT view_name FROM user_views';
default:
return null;
}
}
 
// }}}
// {{{ quoteFloat()
 
/**
* Formats a float value for use within a query in a locale-independent
* manner.
*
* @param float the float value to be quoted.
* @return string the quoted string.
* @see DB_common::quoteSmart()
* @since Method available since release 1.7.8.
*/
function quoteFloat($float) {
return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
}
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/ibase.php
Новый файл
0,0 → 1,1082
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's interbase extension
* for interacting with Interbase and Firebird databases
*
* While this class works with PHP 4, PHP's InterBase extension is
* unstable in PHP 4. Use PHP 5.
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Sterling Hughes <sterling@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: ibase.php,v 1.116 2007/09/21 13:40:41 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's interbase extension
* for interacting with Interbase and Firebird databases
*
* These methods overload the ones declared in DB_common.
*
* While this class works with PHP 4, PHP's InterBase extension is
* unstable in PHP 4. Use PHP 5.
*
* NOTICE: limitQuery() only works for Firebird.
*
* @category Database
* @package DB
* @author Sterling Hughes <sterling@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
* @since Class became stable in Release 1.7.0
*/
class DB_ibase extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'ibase';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'ibase';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* NOTE: only firebird supports limit.
*
* @var array
*/
var $features = array(
'limit' => false,
'new_link' => false,
'numrows' => 'emulate',
'pconnect' => true,
'prepare' => true,
'ssl' => false,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
-104 => DB_ERROR_SYNTAX,
-150 => DB_ERROR_ACCESS_VIOLATION,
-151 => DB_ERROR_ACCESS_VIOLATION,
-155 => DB_ERROR_NOSUCHTABLE,
-157 => DB_ERROR_NOSUCHFIELD,
-158 => DB_ERROR_VALUE_COUNT_ON_ROW,
-170 => DB_ERROR_MISMATCH,
-171 => DB_ERROR_MISMATCH,
-172 => DB_ERROR_INVALID,
// -204 => // Covers too many errors, need to use regex on msg
-205 => DB_ERROR_NOSUCHFIELD,
-206 => DB_ERROR_NOSUCHFIELD,
-208 => DB_ERROR_INVALID,
-219 => DB_ERROR_NOSUCHTABLE,
-297 => DB_ERROR_CONSTRAINT,
-303 => DB_ERROR_INVALID,
-413 => DB_ERROR_INVALID_NUMBER,
-530 => DB_ERROR_CONSTRAINT,
-551 => DB_ERROR_ACCESS_VIOLATION,
-552 => DB_ERROR_ACCESS_VIOLATION,
// -607 => // Covers too many errors, need to use regex on msg
-625 => DB_ERROR_CONSTRAINT_NOT_NULL,
-803 => DB_ERROR_CONSTRAINT,
-804 => DB_ERROR_VALUE_COUNT_ON_ROW,
// -902 => // Covers too many errors, need to use regex on msg
-904 => DB_ERROR_CONNECT_FAILED,
-922 => DB_ERROR_NOSUCHDB,
-923 => DB_ERROR_CONNECT_FAILED,
-924 => DB_ERROR_CONNECT_FAILED
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* The number of rows affected by a data manipulation query
* @var integer
* @access private
*/
var $affected = 0;
 
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
var $autocommit = true;
 
/**
* The prepared statement handle from the most recently executed statement
*
* {@internal Mainly here because the InterBase/Firebird API is only
* able to retrieve data from result sets if the statemnt handle is
* still in scope.}}
*
* @var resource
*/
var $last_stmt;
 
/**
* Is the given prepared statement a data manipulation query?
* @var array
* @access private
*/
var $manip_query = array();
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_ibase()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* PEAR DB's ibase driver supports the following extra DSN options:
* + buffers The number of database buffers to allocate for the
* server-side cache.
* + charset The default character set for a database.
* + dialect The default SQL dialect for any statement
* executed within a connection. Defaults to the
* highest one supported by client libraries.
* Functional only with InterBase 6 and up.
* + role Functional only with InterBase 5 and up.
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('interbase')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
if ($this->dbsyntax == 'firebird') {
$this->features['limit'] = 'alter';
}
 
$params = array(
$dsn['hostspec']
? ($dsn['hostspec'] . ':' . $dsn['database'])
: $dsn['database'],
$dsn['username'] ? $dsn['username'] : null,
$dsn['password'] ? $dsn['password'] : null,
isset($dsn['charset']) ? $dsn['charset'] : null,
isset($dsn['buffers']) ? $dsn['buffers'] : null,
isset($dsn['dialect']) ? $dsn['dialect'] : null,
isset($dsn['role']) ? $dsn['role'] : null,
);
 
$connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
 
$this->connection = @call_user_func_array($connect_function, $params);
if (!$this->connection) {
return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @ibase_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$ismanip = $this->_checkManip($query);
$this->last_query = $query;
$query = $this->modifyQuery($query);
$result = @ibase_query($this->connection, $query);
 
if (!$result) {
return $this->ibaseRaiseError();
}
if ($this->autocommit && $ismanip) {
@ibase_commit($this->connection);
}
if ($ismanip) {
$this->affected = $result;
return DB_OK;
} else {
$this->affected = 0;
return $result;
}
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* Adds LIMIT clauses to a query string according to current DBMS standards
*
* Only works with Firebird.
*
* @param string $query the query to modify
* @param int $from the row to start to fetching (0 = the first row)
* @param int $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return string the query string with LIMIT clauses added
*
* @access protected
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
if ($this->dsn['dbsyntax'] == 'firebird') {
$query = preg_replace('/^([\s(])*SELECT/i',
"SELECT FIRST $count SKIP $from", $query);
}
return $query;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal ibase result pointer to the next available result
*
* @param a valid fbsql result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
if (function_exists('ibase_fetch_assoc')) {
$arr = @ibase_fetch_assoc($result);
} else {
$arr = get_object_vars(ibase_fetch_object($result));
}
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @ibase_fetch_row($result);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? ibase_free_result($result) : false;
}
 
// }}}
// {{{ freeQuery()
 
function freeQuery($query)
{
return is_resource($query) ? ibase_free_query($query) : false;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if (is_integer($this->affected)) {
return $this->affected;
}
return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @ibase_num_fields($result);
if (!$cols) {
return $this->ibaseRaiseError();
}
return $cols;
}
 
// }}}
// {{{ prepare()
 
/**
* Prepares a query for multiple execution with execute().
*
* prepare() requires a generic query as string like <code>
* INSERT INTO numbers VALUES (?, ?, ?)
* </code>. The <kbd>?</kbd> characters are placeholders.
*
* Three types of placeholders can be used:
* + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
* + <kbd>!</kbd> value is inserted 'as is'
* + <kbd>&</kbd> requires a file name. The file's contents get
* inserted into the query (i.e. saving binary
* data in a db)
*
* Use backslashes to escape placeholder characters if you don't want
* them to be interpreted as placeholders. Example: <code>
* "UPDATE foo SET col=? WHERE col='over \& under'"
* </code>
*
* @param string $query query to be prepared
* @return mixed DB statement resource on success. DB_Error on failure.
*/
function prepare($query)
{
$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
PREG_SPLIT_DELIM_CAPTURE);
$token = 0;
$types = array();
$newquery = '';
 
foreach ($tokens as $key => $val) {
switch ($val) {
case '?':
$types[$token++] = DB_PARAM_SCALAR;
break;
case '&':
$types[$token++] = DB_PARAM_OPAQUE;
break;
case '!':
$types[$token++] = DB_PARAM_MISC;
break;
default:
$tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
$newquery .= $tokens[$key] . '?';
}
}
 
$newquery = substr($newquery, 0, -1);
$this->last_query = $query;
$newquery = $this->modifyQuery($newquery);
$stmt = @ibase_prepare($this->connection, $newquery);
 
if ($stmt === false) {
$stmt = $this->ibaseRaiseError();
} else {
$this->prepare_types[(int)$stmt] = $types;
$this->manip_query[(int)$stmt] = DB::isManip($query);
}
 
return $stmt;
}
 
// }}}
// {{{ execute()
 
/**
* Executes a DB statement prepared with prepare().
*
* @param resource $stmt a DB statement resource returned from prepare()
* @param mixed $data array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 for non-array items or the
* quantity of elements in the array.
* @return object a new DB_Result or a DB_Error when fail
* @see DB_ibase::prepare()
* @access public
*/
function &execute($stmt, $data = array())
{
$data = (array)$data;
$this->last_parameters = $data;
 
$types = $this->prepare_types[(int)$stmt];
if (count($types) != count($data)) {
$tmp = $this->raiseError(DB_ERROR_MISMATCH);
return $tmp;
}
 
$i = 0;
foreach ($data as $key => $value) {
if ($types[$i] == DB_PARAM_MISC) {
/*
* ibase doesn't seem to have the ability to pass a
* parameter along unchanged, so strip off quotes from start
* and end, plus turn two single quotes to one single quote,
* in order to avoid the quotes getting escaped by
* ibase and ending up in the database.
*/
$data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
$data[$key] = str_replace("''", "'", $data[$key]);
} elseif ($types[$i] == DB_PARAM_OPAQUE) {
$fp = @fopen($data[$key], 'rb');
if (!$fp) {
$tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
return $tmp;
}
$data[$key] = fread($fp, filesize($data[$key]));
fclose($fp);
}
$i++;
}
 
array_unshift($data, $stmt);
 
$res = call_user_func_array('ibase_execute', $data);
if (!$res) {
$tmp = $this->ibaseRaiseError();
return $tmp;
}
/* XXX need this?
if ($this->autocommit && $this->manip_query[(int)$stmt]) {
@ibase_commit($this->connection);
}*/
$this->last_stmt = $stmt;
if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
$this->_last_query_manip = true;
$this->_next_query_manip = false;
$tmp = DB_OK;
} else {
$this->_last_query_manip = false;
$tmp = new DB_result($this, $res);
}
return $tmp;
}
 
/**
* Frees the internal resources associated with a prepared query
*
* @param resource $stmt the prepared statement's PHP resource
* @param bool $free_resource should the PHP resource be freed too?
* Use false if you need to get data
* from the result set later.
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_ibase::prepare()
*/
function freePrepared($stmt, $free_resource = true)
{
if (!is_resource($stmt)) {
return false;
}
if ($free_resource) {
@ibase_free_query($stmt);
}
unset($this->prepare_tokens[(int)$stmt]);
unset($this->prepare_types[(int)$stmt]);
unset($this->manip_query[(int)$stmt]);
return true;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = false)
{
$this->autocommit = $onoff ? 1 : 0;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
return @ibase_commit($this->connection);
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
return @ibase_rollback($this->connection);
}
 
// }}}
// {{{ transactionInit()
 
function transactionInit($trans_args = 0)
{
return $trans_args
? @ibase_trans($trans_args, $this->connection)
: @ibase_trans();
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_ibase::createSequence(), DB_ibase::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$sqn = strtoupper($this->getSequenceName($seq_name));
$repeat = 0;
do {
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("SELECT GEN_ID(${sqn}, 1) "
. 'FROM RDB$GENERATORS '
. "WHERE RDB\$GENERATOR_NAME='${sqn}'");
$this->popErrorHandling();
if ($ondemand && DB::isError($result)) {
$repeat = 1;
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $result;
}
} else {
$repeat = 0;
}
} while ($repeat);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
$result->free();
return $arr[0];
}
 
// }}}
// {{{ createSequence()
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_ibase::nextID(), DB_ibase::dropSequence()
*/
function createSequence($seq_name)
{
$sqn = strtoupper($this->getSequenceName($seq_name));
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("CREATE GENERATOR ${sqn}");
$this->popErrorHandling();
 
return $result;
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_ibase::nextID(), DB_ibase::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DELETE FROM RDB$GENERATORS '
. "WHERE RDB\$GENERATOR_NAME='"
. strtoupper($this->getSequenceName($seq_name))
. "'");
}
 
// }}}
// {{{ _ibaseFieldFlags()
 
/**
* Get the column's flags
*
* Supports "primary_key", "unique_key", "not_null", "default",
* "computed" and "blob".
*
* @param string $field_name the name of the field
* @param string $table_name the name of the table
*
* @return string the flags
*
* @access private
*/
function _ibaseFieldFlags($field_name, $table_name)
{
$sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
.' FROM RDB$INDEX_SEGMENTS I'
.' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
.' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
.' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
 
$result = @ibase_query($this->connection, $sql);
if (!$result) {
return $this->ibaseRaiseError();
}
 
$flags = '';
if ($obj = @ibase_fetch_object($result)) {
@ibase_free_result($result);
if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') {
$flags .= 'primary_key ';
}
if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') {
$flags .= 'unique_key ';
}
}
 
$sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
.' R.RDB$DEFAULT_SOURCE AS DSOURCE,'
.' F.RDB$FIELD_TYPE AS FTYPE,'
.' F.RDB$COMPUTED_SOURCE AS CSOURCE'
.' FROM RDB$RELATION_FIELDS R '
.' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
.' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
.' AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
 
$result = @ibase_query($this->connection, $sql);
if (!$result) {
return $this->ibaseRaiseError();
}
if ($obj = @ibase_fetch_object($result)) {
@ibase_free_result($result);
if (isset($obj->NFLAG)) {
$flags .= 'not_null ';
}
if (isset($obj->DSOURCE)) {
$flags .= 'default ';
}
if (isset($obj->CSOURCE)) {
$flags .= 'computed ';
}
if (isset($obj->FTYPE) && $obj->FTYPE == 261) {
$flags .= 'blob ';
}
}
 
return trim($flags);
}
 
// }}}
// {{{ ibaseRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_ibase::errorNative(), DB_ibase::errorCode()
*/
function &ibaseRaiseError($errno = null)
{
if ($errno === null) {
$errno = $this->errorCode($this->errorNative());
}
$tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
return $tmp;
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error code produced by the last query
*
* @return int the DBMS' error code. NULL if there is no error code.
*
* @since Method available since Release 1.7.0
*/
function errorNative()
{
if (function_exists('ibase_errcode')) {
return @ibase_errcode();
}
if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
@ibase_errmsg(), $m)) {
return (int)$m[1];
}
return null;
}
 
// }}}
// {{{ errorCode()
 
/**
* Maps native error codes to DB's portable ones
*
* @param int $nativecode the error code returned by the DBMS
*
* @return int the portable DB error code. Return DB_ERROR if the
* current driver doesn't have a mapping for the
* $nativecode submitted.
*
* @since Method available since Release 1.7.0
*/
function errorCode($nativecode = null)
{
if (isset($this->errorcode_map[$nativecode])) {
return $this->errorcode_map[$nativecode];
}
 
static $error_regexps;
if (!isset($error_regexps)) {
$error_regexps = array(
'/generator .* is not defined/'
=> DB_ERROR_SYNTAX, // for compat. w ibase_errcode()
'/table.*(not exist|not found|unknown)/i'
=> DB_ERROR_NOSUCHTABLE,
'/table .* already exists/i'
=> DB_ERROR_ALREADY_EXISTS,
'/unsuccessful metadata update .* failed attempt to store duplicate value/i'
=> DB_ERROR_ALREADY_EXISTS,
'/unsuccessful metadata update .* not found/i'
=> DB_ERROR_NOT_FOUND,
'/validation error for column .* value "\*\*\* null/i'
=> DB_ERROR_CONSTRAINT_NOT_NULL,
'/violation of [\w ]+ constraint/i'
=> DB_ERROR_CONSTRAINT,
'/conversion error from string/i'
=> DB_ERROR_INVALID_NUMBER,
'/no permission for/i'
=> DB_ERROR_ACCESS_VIOLATION,
'/arithmetic exception, numeric overflow, or string truncation/i'
=> DB_ERROR_INVALID,
'/feature is not supported/i'
=> DB_ERROR_NOT_CAPABLE,
);
}
 
$errormsg = @ibase_errmsg();
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $errormsg)) {
return $code;
}
}
return DB_ERROR;
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* NOTE: only supports 'table' and 'flags' if <var>$result</var>
* is a table name.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @ibase_query($this->connection,
"SELECT * FROM $result WHERE 1=0");
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @ibase_num_fields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$info = @ibase_field_info($id, $i);
$res[$i] = array(
'table' => $got_string ? $case_func($result) : '',
'name' => $case_func($info['name']),
'type' => $info['type'],
'len' => $info['length'],
'flags' => ($got_string)
? $this->_ibaseFieldFlags($info['name'], $result)
: '',
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@ibase_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
. 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
case 'views':
return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
case 'users':
return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
default:
return null;
}
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/mysql.php
27,7 → 27,7
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'lib/DB/common.php';
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's mysql extension
/trunk/lib/DB/ifx.php
Новый файл
0,0 → 1,683
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's ifx extension
* for interacting with Informix databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Tomas V.V.Cox <cox@idecnet.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: ifx.php,v 1.75 2007/07/06 05:19:21 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's ifx extension
* for interacting with Informix databases
*
* These methods overload the ones declared in DB_common.
*
* More info on Informix errors can be found at:
* http://www.informix.com/answers/english/ierrors.htm
*
* TODO:
* - set needed env Informix vars on connect
* - implement native prepare/execute
*
* @category Database
* @package DB
* @author Tomas V.V.Cox <cox@idecnet.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_ifx extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'ifx';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'ifx';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'emulate',
'new_link' => false,
'numrows' => 'emulate',
'pconnect' => true,
'prepare' => false,
'ssl' => false,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
'-201' => DB_ERROR_SYNTAX,
'-206' => DB_ERROR_NOSUCHTABLE,
'-217' => DB_ERROR_NOSUCHFIELD,
'-236' => DB_ERROR_VALUE_COUNT_ON_ROW,
'-239' => DB_ERROR_CONSTRAINT,
'-253' => DB_ERROR_SYNTAX,
'-268' => DB_ERROR_CONSTRAINT,
'-292' => DB_ERROR_CONSTRAINT_NOT_NULL,
'-310' => DB_ERROR_ALREADY_EXISTS,
'-316' => DB_ERROR_ALREADY_EXISTS,
'-319' => DB_ERROR_NOT_FOUND,
'-329' => DB_ERROR_NODBSELECTED,
'-346' => DB_ERROR_CONSTRAINT,
'-386' => DB_ERROR_CONSTRAINT_NOT_NULL,
'-391' => DB_ERROR_CONSTRAINT_NOT_NULL,
'-554' => DB_ERROR_SYNTAX,
'-691' => DB_ERROR_CONSTRAINT,
'-692' => DB_ERROR_CONSTRAINT,
'-703' => DB_ERROR_CONSTRAINT_NOT_NULL,
'-1202' => DB_ERROR_DIVZERO,
'-1204' => DB_ERROR_INVALID_DATE,
'-1205' => DB_ERROR_INVALID_DATE,
'-1206' => DB_ERROR_INVALID_DATE,
'-1209' => DB_ERROR_INVALID_DATE,
'-1210' => DB_ERROR_INVALID_DATE,
'-1212' => DB_ERROR_INVALID_DATE,
'-1213' => DB_ERROR_INVALID_NUMBER,
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
var $autocommit = true;
 
/**
* The quantity of transactions begun
*
* {@internal While this is private, it can't actually be designated
* private in PHP 5 because it is directly accessed in the test suite.}}
*
* @var integer
* @access private
*/
var $transaction_opcount = 0;
 
/**
* The number of rows affected by a data manipulation query
* @var integer
* @access private
*/
var $affected = 0;
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_ifx()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('informix') &&
!PEAR::loadExtension('Informix'))
{
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
$dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : '';
$dbname = $dsn['database'] ? $dsn['database'] . $dbhost : '';
$user = $dsn['username'] ? $dsn['username'] : '';
$pw = $dsn['password'] ? $dsn['password'] : '';
 
$connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
 
$this->connection = @$connect_function($dbname, $user, $pw);
if (!is_resource($this->connection)) {
return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED);
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @ifx_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$ismanip = $this->_checkManip($query);
$this->last_query = $query;
$this->affected = null;
if (preg_match('/(SELECT|EXECUTE)/i', $query)) { //TESTME: Use !DB::isManip()?
// the scroll is needed for fetching absolute row numbers
// in a select query result
$result = @ifx_query($query, $this->connection, IFX_SCROLL);
} else {
if (!$this->autocommit && $ismanip) {
if ($this->transaction_opcount == 0) {
$result = @ifx_query('BEGIN WORK', $this->connection);
if (!$result) {
return $this->ifxRaiseError();
}
}
$this->transaction_opcount++;
}
$result = @ifx_query($query, $this->connection);
}
if (!$result) {
return $this->ifxRaiseError();
}
$this->affected = @ifx_affected_rows($result);
// Determine which queries should return data, and which
// should return an error code only.
if (preg_match('/(SELECT|EXECUTE)/i', $query)) {
return $result;
}
// XXX Testme: free results inside a transaction
// may cause to stop it and commit the work?
 
// Result has to be freed even with a insert or update
@ifx_free_result($result);
 
return DB_OK;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal ifx result pointer to the next available result
*
* @param a valid fbsql result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if ($this->_last_query_manip) {
return $this->affected;
} else {
return 0;
}
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if (($rownum !== null) && ($rownum < 0)) {
return null;
}
if ($rownum === null) {
/*
* Even though fetch_row() should return the next row if
* $rownum is null, it doesn't in all cases. Bug 598.
*/
$rownum = 'NEXT';
} else {
// Index starts at row 1, unlike most DBMS's starting at 0.
$rownum++;
}
if (!$arr = @ifx_fetch_row($result, $rownum)) {
return null;
}
if ($fetchmode !== DB_FETCHMODE_ASSOC) {
$i=0;
$order = array();
foreach ($arr as $val) {
$order[$i++] = $val;
}
$arr = $order;
} elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
$this->options['portability'] & DB_PORTABILITY_LOWERCASE)
{
$arr = array_change_key_case($arr, CASE_LOWER);
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
if (!$cols = @ifx_num_fields($result)) {
return $this->ifxRaiseError();
}
return $cols;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? ifx_free_result($result) : false;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = true)
{
// XXX if $this->transaction_opcount > 0, we should probably
// issue a warning here.
$this->autocommit = $onoff ? true : false;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
if ($this->transaction_opcount > 0) {
$result = @ifx_query('COMMIT WORK', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->ifxRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
if ($this->transaction_opcount > 0) {
$result = @ifx_query('ROLLBACK WORK', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->ifxRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ ifxRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_ifx::errorNative(), DB_ifx::errorCode()
*/
function ifxRaiseError($errno = null)
{
if ($errno === null) {
$errno = $this->errorCode(ifx_error());
}
return $this->raiseError($errno, null, null, null,
$this->errorNative());
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error code and message produced by the last query
*
* @return string the DBMS' error code and message
*/
function errorNative()
{
return @ifx_error() . ' ' . @ifx_errormsg();
}
 
// }}}
// {{{ errorCode()
 
/**
* Maps native error codes to DB's portable ones.
*
* Requires that the DB implementation's constructor fills
* in the <var>$errorcode_map</var> property.
*
* @param string $nativecode error code returned by the database
* @return int a portable DB error code, or DB_ERROR if this DB
* implementation has no mapping for the given error code.
*/
function errorCode($nativecode)
{
if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
$code = $match[1];
if (isset($this->errorcode_map[$code])) {
return $this->errorcode_map[$code];
}
}
return DB_ERROR;
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* NOTE: only supports 'table' if <var>$result</var> is a table name.
*
* If analyzing a query result and the result has duplicate field names,
* an error will be raised saying
* <samp>can't distinguish duplicate field names</samp>.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.6.0
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @ifx_query("SELECT * FROM $result WHERE 1=0",
$this->connection);
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
$flds = @ifx_fieldproperties($id);
$count = @ifx_num_fields($id);
 
if (count($flds) != $count) {
return $this->raiseError("can't distinguish duplicate field names");
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$i = 0;
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
foreach ($flds as $key => $value) {
$props = explode(';', $value);
$res[$i] = array(
'table' => $got_string ? $case_func($result) : '',
'name' => $case_func($key),
'type' => $props[0],
'len' => $props[1],
'flags' => $props[4] == 'N' ? 'not_null' : '',
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
$i++;
}
 
// free the result only if we were called on a table
if ($got_string) {
@ifx_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SELECT tabname FROM systables WHERE tabid >= 100';
default:
return null;
}
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/pgsql.php
Новый файл
0,0 → 1,1116
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's pgsql extension
* for interacting with PostgreSQL databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Rui Hirokawa <hirokawa@php.net>
* @author Stig Bakken <ssb@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: pgsql.php,v 1.138 2007/09/21 13:40:41 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's pgsql extension
* for interacting with PostgreSQL databases
*
* These methods overload the ones declared in DB_common.
*
* @category Database
* @package DB
* @author Rui Hirokawa <hirokawa@php.net>
* @author Stig Bakken <ssb@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_pgsql extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'pgsql';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'pgsql';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'alter',
'new_link' => '4.3.0',
'numrows' => true,
'pconnect' => true,
'prepare' => false,
'ssl' => true,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
var $autocommit = true;
 
/**
* The quantity of transactions begun
*
* {@internal While this is private, it can't actually be designated
* private in PHP 5 because it is directly accessed in the test suite.}}
*
* @var integer
* @access private
*/
var $transaction_opcount = 0;
 
/**
* The number of rows affected by a data manipulation query
* @var integer
*/
var $affected = 0;
 
/**
* The current row being looked at in fetchInto()
* @var array
* @access private
*/
var $row = array();
 
/**
* The number of rows in a given result set
* @var array
* @access private
*/
var $_num_rows = array();
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_pgsql()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* PEAR DB's pgsql driver supports the following extra DSN options:
* + connect_timeout How many seconds to wait for a connection to
* be established. Available since PEAR DB 1.7.0.
* + new_link If set to true, causes subsequent calls to
* connect() to return a new connection link
* instead of the existing one. WARNING: this is
* not portable to other DBMS's. Available only
* if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0.
* + options Command line options to be sent to the server.
* Available since PEAR DB 1.6.4.
* + service Specifies a service name in pg_service.conf that
* holds additional connection parameters.
* Available since PEAR DB 1.7.0.
* + sslmode How should SSL be used when connecting? Values:
* disable, allow, prefer or require.
* Available since PEAR DB 1.7.0.
* + tty This was used to specify where to send server
* debug output. Available since PEAR DB 1.6.4.
*
* Example of connecting to a new link via a socket:
* <code>
* require_once 'DB.php';
*
* $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
* $options = array(
* 'portability' => DB_PORTABILITY_ALL,
* );
*
* $db = DB::connect($dsn, $options);
* if (PEAR::isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('pgsql')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
$protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp';
 
$params = array('');
if ($protocol == 'tcp') {
if ($dsn['hostspec']) {
$params[0] .= 'host=' . $dsn['hostspec'];
}
if ($dsn['port']) {
$params[0] .= ' port=' . $dsn['port'];
}
} elseif ($protocol == 'unix') {
// Allow for pg socket in non-standard locations.
if ($dsn['socket']) {
$params[0] .= 'host=' . $dsn['socket'];
}
if ($dsn['port']) {
$params[0] .= ' port=' . $dsn['port'];
}
}
if ($dsn['database']) {
$params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\'';
}
if ($dsn['username']) {
$params[0] .= ' user=\'' . addslashes($dsn['username']) . '\'';
}
if ($dsn['password']) {
$params[0] .= ' password=\'' . addslashes($dsn['password']) . '\'';
}
if (!empty($dsn['options'])) {
$params[0] .= ' options=' . $dsn['options'];
}
if (!empty($dsn['tty'])) {
$params[0] .= ' tty=' . $dsn['tty'];
}
if (!empty($dsn['connect_timeout'])) {
$params[0] .= ' connect_timeout=' . $dsn['connect_timeout'];
}
if (!empty($dsn['sslmode'])) {
$params[0] .= ' sslmode=' . $dsn['sslmode'];
}
if (!empty($dsn['service'])) {
$params[0] .= ' service=' . $dsn['service'];
}
 
if (isset($dsn['new_link'])
&& ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
{
if (version_compare(phpversion(), '4.3.0', '>=')) {
$params[] = PGSQL_CONNECT_FORCE_NEW;
}
}
 
$connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
 
$ini = ini_get('track_errors');
$php_errormsg = '';
if ($ini) {
$this->connection = @call_user_func_array($connect_function,
$params);
} else {
@ini_set('track_errors', 1);
$this->connection = @call_user_func_array($connect_function,
$params);
@ini_set('track_errors', $ini);
}
 
if (!$this->connection) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$php_errormsg);
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @pg_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$ismanip = $this->_checkManip($query);
$this->last_query = $query;
$query = $this->modifyQuery($query);
if (!$this->autocommit && $ismanip) {
if ($this->transaction_opcount == 0) {
$result = @pg_exec($this->connection, 'begin;');
if (!$result) {
return $this->pgsqlRaiseError();
}
}
$this->transaction_opcount++;
}
$result = @pg_exec($this->connection, $query);
if (!$result) {
return $this->pgsqlRaiseError();
}
 
/*
* Determine whether queries produce affected rows, result or nothing.
*
* This logic was introduced in version 1.1 of the file by ssb,
* though the regex has been modified slightly since then.
*
* PostgreSQL commands:
* ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
* CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
* GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
* REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
* UNLISTEN, UPDATE, VACUUM
*/
if ($ismanip) {
$this->affected = @pg_affected_rows($result);
return DB_OK;
} elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW)\s/si',
$query))
{
$this->row[(int)$result] = 0; // reset the row counter.
$numrows = $this->numRows($result);
if (is_object($numrows)) {
return $numrows;
}
$this->_num_rows[(int)$result] = $numrows;
$this->affected = 0;
return $result;
} else {
$this->affected = 0;
return DB_OK;
}
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal pgsql result pointer to the next available result
*
* @param a valid fbsql result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
$result_int = (int)$result;
$rownum = ($rownum !== null) ? $rownum : $this->row[$result_int];
if ($rownum >= $this->_num_rows[$result_int]) {
return null;
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @pg_fetch_row($result, $rownum);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
$this->row[$result_int] = ++$rownum;
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
if (is_resource($result)) {
unset($this->row[(int)$result]);
unset($this->_num_rows[(int)$result]);
$this->affected = 0;
return @pg_freeresult($result);
}
return false;
}
 
// }}}
// {{{ quote()
 
/**
* @deprecated Deprecated in release 1.6.0
* @internal
*/
function quote($str)
{
return $this->quoteSmart($str);
}
 
// }}}
// {{{ quoteBoolean()
 
/**
* Formats a boolean value for use within a query in a locale-independent
* manner.
*
* @param boolean the boolean value to be quoted.
* @return string the quoted string.
* @see DB_common::quoteSmart()
* @since Method available since release 1.7.8.
*/
function quoteBoolean($boolean) {
return $boolean ? 'TRUE' : 'FALSE';
}
// }}}
// {{{ escapeSimple()
 
/**
* Escapes a string according to the current DBMS's standards
*
* {@internal PostgreSQL treats a backslash as an escape character,
* so they are escaped as well.
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @see DB_common::quoteSmart()
* @since Method available since Release 1.6.0
*/
function escapeSimple($str)
{
if (function_exists('pg_escape_string')) {
/* This fixes an undocumented BC break in PHP 5.2.0 which changed
* the prototype of pg_escape_string. I'm not thrilled about having
* to sniff the PHP version, quite frankly, but it's the only way
* to deal with the problem. Revision 1.331.2.13.2.10 on
* php-src/ext/pgsql/pgsql.c (PHP_5_2 branch) is to blame, for the
* record. */
if (version_compare(PHP_VERSION, '5.2.0', '>=')) {
return pg_escape_string($this->connection, $str);
} else {
return pg_escape_string($str);
}
} else {
return str_replace("'", "''", str_replace('\\', '\\\\', $str));
}
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @pg_numfields($result);
if (!$cols) {
return $this->pgsqlRaiseError();
}
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$rows = @pg_numrows($result);
if ($rows === null) {
return $this->pgsqlRaiseError();
}
return $rows;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = false)
{
// XXX if $this->transaction_opcount > 0, we should probably
// issue a warning here.
$this->autocommit = $onoff ? true : false;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
if ($this->transaction_opcount > 0) {
// (disabled) hack to shut up error messages from libpq.a
//@fclose(@fopen("php://stderr", "w"));
$result = @pg_exec($this->connection, 'end;');
$this->transaction_opcount = 0;
if (!$result) {
return $this->pgsqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
if ($this->transaction_opcount > 0) {
$result = @pg_exec($this->connection, 'abort;');
$this->transaction_opcount = 0;
if (!$result) {
return $this->pgsqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
return $this->affected;
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_pgsql::createSequence(), DB_pgsql::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
$repeat = false;
do {
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("SELECT NEXTVAL('${seqname}')");
$this->popErrorHandling();
if ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE) {
$repeat = true;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->createSequence($seq_name);
$this->popErrorHandling();
if (DB::isError($result)) {
return $this->raiseError($result);
}
} else {
$repeat = false;
}
} while ($repeat);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
$result->free();
return $arr[0];
}
 
// }}}
// {{{ createSequence()
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_pgsql::nextID(), DB_pgsql::dropSequence()
*/
function createSequence($seq_name)
{
$seqname = $this->getSequenceName($seq_name);
$result = $this->query("CREATE SEQUENCE ${seqname}");
return $result;
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_pgsql::nextID(), DB_pgsql::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP SEQUENCE '
. $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* Adds LIMIT clauses to a query string according to current DBMS standards
*
* @param string $query the query to modify
* @param int $from the row to start to fetching (0 = the first row)
* @param int $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return string the query string with LIMIT clauses added
*
* @access protected
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
return "$query LIMIT $count OFFSET $from";
}
 
// }}}
// {{{ pgsqlRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_pgsql::errorNative(), DB_pgsql::errorCode()
*/
function pgsqlRaiseError($errno = null)
{
$native = $this->errorNative();
if (!$native) {
$native = 'Database connection has been lost.';
$errno = DB_ERROR_CONNECT_FAILED;
}
if ($errno === null) {
$errno = $this->errorCode($native);
}
return $this->raiseError($errno, null, null, null, $native);
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error message produced by the last query
*
* {@internal Error messages are used instead of error codes
* in order to support older versions of PostgreSQL.}}
*
* @return string the DBMS' error message
*/
function errorNative()
{
return @pg_errormessage($this->connection);
}
 
// }}}
// {{{ errorCode()
 
/**
* Determines PEAR::DB error code from the database's text error message.
*
* @param string $errormsg error message returned from the database
* @return integer an error number from a DB error constant
*/
function errorCode($errormsg)
{
static $error_regexps;
if (!isset($error_regexps)) {
$error_regexps = array(
'/column .* (of relation .*)?does not exist/i'
=> DB_ERROR_NOSUCHFIELD,
'/(relation|sequence|table).*does not exist|class .* not found/i'
=> DB_ERROR_NOSUCHTABLE,
'/index .* does not exist/'
=> DB_ERROR_NOT_FOUND,
'/relation .* already exists/i'
=> DB_ERROR_ALREADY_EXISTS,
'/(divide|division) by zero$/i'
=> DB_ERROR_DIVZERO,
'/pg_atoi: error in .*: can\'t parse /i'
=> DB_ERROR_INVALID_NUMBER,
'/invalid input syntax for( type)? (integer|numeric)/i'
=> DB_ERROR_INVALID_NUMBER,
'/value .* is out of range for type \w*int/i'
=> DB_ERROR_INVALID_NUMBER,
'/integer out of range/i'
=> DB_ERROR_INVALID_NUMBER,
'/value too long for type character/i'
=> DB_ERROR_INVALID,
'/attribute .* not found|relation .* does not have attribute/i'
=> DB_ERROR_NOSUCHFIELD,
'/column .* specified in USING clause does not exist in (left|right) table/i'
=> DB_ERROR_NOSUCHFIELD,
'/parser: parse error at or near/i'
=> DB_ERROR_SYNTAX,
'/syntax error at/'
=> DB_ERROR_SYNTAX,
'/column reference .* is ambiguous/i'
=> DB_ERROR_SYNTAX,
'/permission denied/'
=> DB_ERROR_ACCESS_VIOLATION,
'/violates not-null constraint/'
=> DB_ERROR_CONSTRAINT_NOT_NULL,
'/violates [\w ]+ constraint/'
=> DB_ERROR_CONSTRAINT,
'/referential integrity violation/'
=> DB_ERROR_CONSTRAINT,
'/more expressions than target columns/i'
=> DB_ERROR_VALUE_COUNT_ON_ROW,
);
}
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $errormsg)) {
return $code;
}
}
// Fall back to DB_ERROR if there was no mapping.
return DB_ERROR;
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* NOTE: only supports 'table' and 'flags' if <var>$result</var>
* is a table name.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @pg_numfields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => $got_string ? $case_func($result) : '',
'name' => $case_func(@pg_fieldname($id, $i)),
'type' => @pg_fieldtype($id, $i),
'len' => @pg_fieldsize($id, $i),
'flags' => $got_string
? $this->_pgFieldFlags($id, $i, $result)
: '',
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@pg_freeresult($id);
}
return $res;
}
 
// }}}
// {{{ _pgFieldFlags()
 
/**
* Get a column's flags
*
* Supports "not_null", "default_value", "primary_key", "unique_key"
* and "multiple_key". The default value is passed through
* rawurlencode() in case there are spaces in it.
*
* @param int $resource the PostgreSQL result identifier
* @param int $num_field the field number
*
* @return string the flags
*
* @access private
*/
function _pgFieldFlags($resource, $num_field, $table_name)
{
$field_name = @pg_fieldname($resource, $num_field);
 
// Check if there's a schema in $table_name and update things
// accordingly.
$from = 'pg_attribute f, pg_class tab, pg_type typ';
if (strpos($table_name, '.') !== false) {
$from .= ', pg_namespace nsp';
list($schema, $table) = explode('.', $table_name);
$tableWhere = "tab.relname = '$table' AND tab.relnamespace = nsp.oid AND nsp.nspname = '$schema'";
} else {
$tableWhere = "tab.relname = '$table_name'";
}
 
$result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
FROM $from
WHERE tab.relname = typ.typname
AND typ.typrelid = f.attrelid
AND f.attname = '$field_name'
AND $tableWhere");
if (@pg_numrows($result) > 0) {
$row = @pg_fetch_row($result, 0);
$flags = ($row[0] == 't') ? 'not_null ' : '';
 
if ($row[1] == 't') {
$result = @pg_exec($this->connection, "SELECT a.adsrc
FROM $from, pg_attrdef a
WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
AND f.attrelid = a.adrelid AND f.attname = '$field_name'
AND $tableWhere AND f.attnum = a.adnum");
$row = @pg_fetch_row($result, 0);
$num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
$flags .= 'default_' . rawurlencode($num) . ' ';
}
} else {
$flags = '';
}
$result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
FROM $from, pg_index i
WHERE tab.relname = typ.typname
AND typ.typrelid = f.attrelid
AND f.attrelid = i.indrelid
AND f.attname = '$field_name'
AND $tableWhere");
$count = @pg_numrows($result);
 
for ($i = 0; $i < $count ; $i++) {
$row = @pg_fetch_row($result, $i);
$keys = explode(' ', $row[2]);
 
if (in_array($num_field + 1, $keys)) {
$flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
$flags .= ($row[1] == 't') ? 'primary_key ' : '';
if (count($keys) > 1)
$flags .= 'multiple_key ';
}
}
 
return trim($flags);
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SELECT c.relname AS "Name"'
. ' FROM pg_class c, pg_user u'
. ' WHERE c.relowner = u.usesysid'
. " AND c.relkind = 'r'"
. ' AND NOT EXISTS'
. ' (SELECT 1 FROM pg_views'
. ' WHERE viewname = c.relname)'
. " AND c.relname !~ '^(pg_|sql_)'"
. ' UNION'
. ' SELECT c.relname AS "Name"'
. ' FROM pg_class c'
. " WHERE c.relkind = 'r'"
. ' AND NOT EXISTS'
. ' (SELECT 1 FROM pg_views'
. ' WHERE viewname = c.relname)'
. ' AND NOT EXISTS'
. ' (SELECT 1 FROM pg_user'
. ' WHERE usesysid = c.relowner)'
. " AND c.relname !~ '^pg_'";
case 'schema.tables':
return "SELECT schemaname || '.' || tablename"
. ' AS "Name"'
. ' FROM pg_catalog.pg_tables'
. ' WHERE schemaname NOT IN'
. " ('pg_catalog', 'information_schema', 'pg_toast')";
case 'schema.views':
return "SELECT schemaname || '.' || viewname from pg_views WHERE schemaname"
. " NOT IN ('information_schema', 'pg_catalog')";
case 'views':
// Table cols: viewname | viewowner | definition
return 'SELECT viewname from pg_views WHERE schemaname'
. " NOT IN ('information_schema', 'pg_catalog')";
case 'users':
// cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil
return 'SELECT usename FROM pg_user';
case 'databases':
return 'SELECT datname FROM pg_database';
case 'functions':
case 'procedures':
return 'SELECT proname FROM pg_proc WHERE proowner <> 1';
default:
return null;
}
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/sybase.php
Новый файл
0,0 → 1,942
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's sybase extension
* for interacting with Sybase databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Sterling Hughes <sterling@php.net>
* @author Antônio Carlos Venâncio Júnior <floripa@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: sybase.php,v 1.87 2007/09/21 13:40:42 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's sybase extension
* for interacting with Sybase databases
*
* These methods overload the ones declared in DB_common.
*
* WARNING: This driver may fail with multiple connections under the
* same user/pass/host and different databases.
*
* @category Database
* @package DB
* @author Sterling Hughes <sterling@php.net>
* @author Antônio Carlos Venâncio Júnior <floripa@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_sybase extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'sybase';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'sybase';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'emulate',
'new_link' => false,
'numrows' => true,
'pconnect' => true,
'prepare' => false,
'ssl' => false,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
var $autocommit = true;
 
/**
* The quantity of transactions begun
*
* {@internal While this is private, it can't actually be designated
* private in PHP 5 because it is directly accessed in the test suite.}}
*
* @var integer
* @access private
*/
var $transaction_opcount = 0;
 
/**
* The database specified in the DSN
*
* It's a fix to allow calls to different databases in the same script.
*
* @var string
* @access private
*/
var $_db = '';
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_sybase()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* PEAR DB's sybase driver supports the following extra DSN options:
* + appname The application name to use on this connection.
* Available since PEAR DB 1.7.0.
* + charset The character set to use on this connection.
* Available since PEAR DB 1.7.0.
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('sybase') &&
!PEAR::loadExtension('sybase_ct'))
{
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
$dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost';
$dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false;
$dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false;
$dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false;
 
$connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect';
 
if ($dsn['username']) {
$this->connection = @$connect_function($dsn['hostspec'],
$dsn['username'],
$dsn['password'],
$dsn['charset'],
$dsn['appname']);
} else {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
'The DSN did not contain a username.');
}
 
if (!$this->connection) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
@sybase_get_last_message());
}
 
if ($dsn['database']) {
if (!@sybase_select_db($dsn['database'], $this->connection)) {
return $this->raiseError(DB_ERROR_NODBSELECTED,
null, null, null,
@sybase_get_last_message());
}
$this->_db = $dsn['database'];
}
 
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @sybase_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$ismanip = $this->_checkManip($query);
$this->last_query = $query;
if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
}
$query = $this->modifyQuery($query);
if (!$this->autocommit && $ismanip) {
if ($this->transaction_opcount == 0) {
$result = @sybase_query('BEGIN TRANSACTION', $this->connection);
if (!$result) {
return $this->sybaseRaiseError();
}
}
$this->transaction_opcount++;
}
$result = @sybase_query($query, $this->connection);
if (!$result) {
return $this->sybaseRaiseError();
}
if (is_resource($result)) {
return $result;
}
// Determine which queries that should return data, and which
// should return an error code only.
return $ismanip ? DB_OK : $result;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal sybase result pointer to the next available result
*
* @param a valid sybase result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
if (!@sybase_data_seek($result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
if (function_exists('sybase_fetch_assoc')) {
$arr = @sybase_fetch_assoc($result);
} else {
if ($arr = @sybase_fetch_array($result)) {
foreach ($arr as $key => $value) {
if (is_int($key)) {
unset($arr[$key]);
}
}
}
}
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @sybase_fetch_row($result);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? sybase_free_result($result) : false;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @sybase_num_fields($result);
if (!$cols) {
return $this->sybaseRaiseError();
}
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$rows = @sybase_num_rows($result);
if ($rows === false) {
return $this->sybaseRaiseError();
}
return $rows;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if ($this->_last_query_manip) {
$result = @sybase_affected_rows($this->connection);
} else {
$result = 0;
}
return $result;
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_sybase::createSequence(), DB_sybase::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
}
$repeat = 0;
do {
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
$this->popErrorHandling();
if ($ondemand && DB::isError($result) &&
($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
{
$repeat = 1;
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $this->raiseError($result);
}
} elseif (!DB::isError($result)) {
$result = $this->query("SELECT @@IDENTITY FROM $seqname");
$repeat = 0;
} else {
$repeat = false;
}
} while ($repeat);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$result = $result->fetchRow(DB_FETCHMODE_ORDERED);
return $result[0];
}
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_sybase::nextID(), DB_sybase::dropSequence()
*/
function createSequence($seq_name)
{
return $this->query('CREATE TABLE '
. $this->getSequenceName($seq_name)
. ' (id numeric(10, 0) IDENTITY NOT NULL,'
. ' vapor int NULL)');
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_sybase::nextID(), DB_sybase::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ quoteFloat()
 
/**
* Formats a float value for use within a query in a locale-independent
* manner.
*
* @param float the float value to be quoted.
* @return string the quoted string.
* @see DB_common::quoteSmart()
* @since Method available since release 1.7.8.
*/
function quoteFloat($float) {
return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
}
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = false)
{
// XXX if $this->transaction_opcount > 0, we should probably
// issue a warning here.
$this->autocommit = $onoff ? true : false;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
if ($this->transaction_opcount > 0) {
if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
}
$result = @sybase_query('COMMIT', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->sybaseRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
if ($this->transaction_opcount > 0) {
if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
}
$result = @sybase_query('ROLLBACK', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->sybaseRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ sybaseRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_sybase::errorNative(), DB_sybase::errorCode()
*/
function sybaseRaiseError($errno = null)
{
$native = $this->errorNative();
if ($errno === null) {
$errno = $this->errorCode($native);
}
return $this->raiseError($errno, null, null, null, $native);
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error message produced by the last query
*
* @return string the DBMS' error message
*/
function errorNative()
{
return @sybase_get_last_message();
}
 
// }}}
// {{{ errorCode()
 
/**
* Determines PEAR::DB error code from the database's text error message.
*
* @param string $errormsg error message returned from the database
* @return integer an error number from a DB error constant
*/
function errorCode($errormsg)
{
static $error_regexps;
// PHP 5.2+ prepends the function name to $php_errormsg, so we need
// this hack to work around it, per bug #9599.
$errormsg = preg_replace('/^sybase[a-z_]+\(\): /', '', $errormsg);
if (!isset($error_regexps)) {
$error_regexps = array(
'/Incorrect syntax near/'
=> DB_ERROR_SYNTAX,
'/^Unclosed quote before the character string [\"\'].*[\"\']\./'
=> DB_ERROR_SYNTAX,
'/Implicit conversion (from datatype|of NUMERIC value)/i'
=> DB_ERROR_INVALID_NUMBER,
'/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./'
=> DB_ERROR_NOSUCHTABLE,
'/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./'
=> DB_ERROR_ACCESS_VIOLATION,
'/^.+ permission denied on object .+, database .+, owner .+/'
=> DB_ERROR_ACCESS_VIOLATION,
'/^.* permission denied, database .+, owner .+/'
=> DB_ERROR_ACCESS_VIOLATION,
'/[^.*] not found\./'
=> DB_ERROR_NOSUCHTABLE,
'/There is already an object named/'
=> DB_ERROR_ALREADY_EXISTS,
'/Invalid column name/'
=> DB_ERROR_NOSUCHFIELD,
'/does not allow null values/'
=> DB_ERROR_CONSTRAINT_NOT_NULL,
'/Command has been aborted/'
=> DB_ERROR_CONSTRAINT,
'/^Cannot drop the index .* because it doesn\'t exist/i'
=> DB_ERROR_NOT_FOUND,
'/^There is already an index/i'
=> DB_ERROR_ALREADY_EXISTS,
'/^There are fewer columns in the INSERT statement than values specified/i'
=> DB_ERROR_VALUE_COUNT_ON_ROW,
'/Divide by zero/i'
=> DB_ERROR_DIVZERO,
);
}
 
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $errormsg)) {
return $code;
}
}
return DB_ERROR;
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* NOTE: only supports 'table' and 'flags' if <var>$result</var>
* is a table name.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.6.0
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
}
$id = @sybase_query("SELECT * FROM $result WHERE 1=0",
$this->connection);
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @sybase_num_fields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$f = @sybase_fetch_field($id, $i);
// column_source is often blank
$res[$i] = array(
'table' => $got_string
? $case_func($result)
: $case_func($f->column_source),
'name' => $case_func($f->name),
'type' => $f->type,
'len' => $f->max_length,
'flags' => '',
);
if ($res[$i]['table']) {
$res[$i]['flags'] = $this->_sybase_field_flags(
$res[$i]['table'], $res[$i]['name']);
}
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@sybase_free_result($id);
}
return $res;
}
 
// }}}
// {{{ _sybase_field_flags()
 
/**
* Get the flags for a field
*
* Currently supports:
* + <samp>unique_key</samp> (unique index, unique check or primary_key)
* + <samp>multiple_key</samp> (multi-key index)
*
* @param string $table the table name
* @param string $column the field name
*
* @return string space delimited string of flags. Empty string if none.
*
* @access private
*/
function _sybase_field_flags($table, $column)
{
static $tableName = null;
static $flags = array();
 
if ($table != $tableName) {
$flags = array();
$tableName = $table;
 
/* We're running sp_helpindex directly because it doesn't exist in
* older versions of ASE -- unfortunately, we can't just use
* DB::isError() because the user may be using callback error
* handling. */
$res = @sybase_query("sp_helpindex $table", $this->connection);
 
if ($res === false || $res === true) {
// Fake a valid response for BC reasons.
return '';
}
 
while (($val = sybase_fetch_assoc($res)) !== false) {
if (!isset($val['index_keys'])) {
/* No useful information returned. Break and be done with
* it, which preserves the pre-1.7.9 behaviour. */
break;
}
 
$keys = explode(', ', trim($val['index_keys']));
 
if (sizeof($keys) > 1) {
foreach ($keys as $key) {
$this->_add_flag($flags[$key], 'multiple_key');
}
}
 
if (strpos($val['index_description'], 'unique')) {
foreach ($keys as $key) {
$this->_add_flag($flags[$key], 'unique_key');
}
}
}
 
sybase_free_result($res);
 
}
 
if (array_key_exists($column, $flags)) {
return(implode(' ', $flags[$column]));
}
 
return '';
}
 
// }}}
// {{{ _add_flag()
 
/**
* Adds a string to the flags array if the flag is not yet in there
* - if there is no flag present the array is created
*
* @param array $array reference of flags array to add a value to
* @param mixed $value value to add to the flag array
*
* @return void
*
* @access private
*/
function _add_flag(&$array, $value)
{
if (!is_array($array)) {
$array = array($value);
} elseif (!in_array($value, $array)) {
array_push($array, $value);
}
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return "SELECT name FROM sysobjects WHERE type = 'U'"
. ' ORDER BY name';
case 'views':
return "SELECT name FROM sysobjects WHERE type = 'V'";
default:
return null;
}
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/fbsql.php
Новый файл
0,0 → 1,769
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's fbsql extension
* for interacting with FrontBase databases
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Frank M. Kromann <frank@frontbase.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: fbsql.php,v 1.88 2007/07/06 05:19:21 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's fbsql extension
* for interacting with FrontBase databases
*
* These methods overload the ones declared in DB_common.
*
* @category Database
* @package DB
* @author Frank M. Kromann <frank@frontbase.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
* @since Class functional since Release 1.7.0
*/
class DB_fbsql extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'fbsql';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'fbsql';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'alter',
'new_link' => false,
'numrows' => true,
'pconnect' => true,
'prepare' => false,
'ssl' => false,
'transactions' => true,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
22 => DB_ERROR_SYNTAX,
85 => DB_ERROR_ALREADY_EXISTS,
108 => DB_ERROR_SYNTAX,
116 => DB_ERROR_NOSUCHTABLE,
124 => DB_ERROR_VALUE_COUNT_ON_ROW,
215 => DB_ERROR_NOSUCHFIELD,
217 => DB_ERROR_INVALID_NUMBER,
226 => DB_ERROR_NOSUCHFIELD,
231 => DB_ERROR_INVALID,
239 => DB_ERROR_TRUNCATED,
251 => DB_ERROR_SYNTAX,
266 => DB_ERROR_NOT_FOUND,
357 => DB_ERROR_CONSTRAINT_NOT_NULL,
358 => DB_ERROR_CONSTRAINT,
360 => DB_ERROR_CONSTRAINT,
361 => DB_ERROR_CONSTRAINT,
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_fbsql()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('fbsql')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
$params = array(
$dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
$dsn['username'] ? $dsn['username'] : null,
$dsn['password'] ? $dsn['password'] : null,
);
 
$connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
 
$ini = ini_get('track_errors');
$php_errormsg = '';
if ($ini) {
$this->connection = @call_user_func_array($connect_function,
$params);
} else {
@ini_set('track_errors', 1);
$this->connection = @call_user_func_array($connect_function,
$params);
@ini_set('track_errors', $ini);
}
 
if (!$this->connection) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$php_errormsg);
}
 
if ($dsn['database']) {
if (!@fbsql_select_db($dsn['database'], $this->connection)) {
return $this->fbsqlRaiseError();
}
}
 
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @fbsql_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$this->last_query = $query;
$query = $this->modifyQuery($query);
$result = @fbsql_query("$query;", $this->connection);
if (!$result) {
return $this->fbsqlRaiseError();
}
// Determine which queries that should return data, and which
// should return an error code only.
if ($this->_checkManip($query)) {
return DB_OK;
}
return $result;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal fbsql result pointer to the next available result
*
* @param a valid fbsql result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return @fbsql_next_result($result);
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
if (!@fbsql_data_seek($result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @fbsql_fetch_array($result, FBSQL_ASSOC);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @fbsql_fetch_row($result);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? fbsql_free_result($result) : false;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff=false)
{
if ($onoff) {
$this->query("SET COMMIT TRUE");
} else {
$this->query("SET COMMIT FALSE");
}
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
@fbsql_commit($this->connection);
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
@fbsql_rollback($this->connection);
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @fbsql_num_fields($result);
if (!$cols) {
return $this->fbsqlRaiseError();
}
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$rows = @fbsql_num_rows($result);
if ($rows === null) {
return $this->fbsqlRaiseError();
}
return $rows;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if ($this->_last_query_manip) {
$result = @fbsql_affected_rows($this->connection);
} else {
$result = 0;
}
return $result;
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_fbsql::createSequence(), DB_fbsql::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
do {
$repeat = 0;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query('SELECT UNIQUE FROM ' . $seqname);
$this->popErrorHandling();
if ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE) {
$repeat = 1;
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $result;
}
} else {
$repeat = 0;
}
} while ($repeat);
if (DB::isError($result)) {
return $this->fbsqlRaiseError();
}
$result->fetchInto($tmp, DB_FETCHMODE_ORDERED);
return $tmp[0];
}
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_fbsql::nextID(), DB_fbsql::dropSequence()
*/
function createSequence($seq_name)
{
$seqname = $this->getSequenceName($seq_name);
$res = $this->query('CREATE TABLE ' . $seqname
. ' (id INTEGER NOT NULL,'
. ' PRIMARY KEY(id))');
if ($res) {
$res = $this->query('SET UNIQUE = 0 FOR ' . $seqname);
}
return $res;
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_fbsql::nextID(), DB_fbsql::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)
. ' RESTRICT');
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* Adds LIMIT clauses to a query string according to current DBMS standards
*
* @param string $query the query to modify
* @param int $from the row to start to fetching (0 = the first row)
* @param int $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return string the query string with LIMIT clauses added
*
* @access protected
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
if (DB::isManip($query) || $this->_next_query_manip) {
return preg_replace('/^([\s(])*SELECT/i',
"\\1SELECT TOP($count)", $query);
} else {
return preg_replace('/([\s(])*SELECT/i',
"\\1SELECT TOP($from, $count)", $query);
}
}
 
// }}}
// {{{ quoteBoolean()
 
/**
* Formats a boolean value for use within a query in a locale-independent
* manner.
*
* @param boolean the boolean value to be quoted.
* @return string the quoted string.
* @see DB_common::quoteSmart()
* @since Method available since release 1.7.8.
*/
function quoteBoolean($boolean) {
return $boolean ? 'TRUE' : 'FALSE';
}
// }}}
// {{{ quoteFloat()
 
/**
* Formats a float value for use within a query in a locale-independent
* manner.
*
* @param float the float value to be quoted.
* @return string the quoted string.
* @see DB_common::quoteSmart()
* @since Method available since release 1.7.8.
*/
function quoteFloat($float) {
return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
}
// }}}
// {{{ fbsqlRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_fbsql::errorNative(), DB_common::errorCode()
*/
function fbsqlRaiseError($errno = null)
{
if ($errno === null) {
$errno = $this->errorCode(fbsql_errno($this->connection));
}
return $this->raiseError($errno, null, null, null,
@fbsql_error($this->connection));
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error code produced by the last query
*
* @return int the DBMS' error code
*/
function errorNative()
{
return @fbsql_errno($this->connection);
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @fbsql_list_fields($this->dsn['database'],
$result, $this->connection);
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @fbsql_num_fields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => $case_func(@fbsql_field_table($id, $i)),
'name' => $case_func(@fbsql_field_name($id, $i)),
'type' => @fbsql_field_type($id, $i),
'len' => @fbsql_field_len($id, $i),
'flags' => @fbsql_field_flags($id, $i),
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@fbsql_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the SQL query string or null if the driver doesn't
* support the object type requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SELECT "table_name" FROM information_schema.tables'
. ' t0, information_schema.schemata t1'
. ' WHERE t0.schema_pk=t1.schema_pk AND'
. ' "table_type" = \'BASE TABLE\''
. ' AND "schema_name" = current_schema';
case 'views':
return 'SELECT "table_name" FROM information_schema.tables'
. ' t0, information_schema.schemata t1'
. ' WHERE t0.schema_pk=t1.schema_pk AND'
. ' "table_type" = \'VIEW\''
. ' AND "schema_name" = current_schema';
case 'users':
return 'SELECT "user_name" from information_schema.users';
case 'functions':
return 'SELECT "routine_name" FROM'
. ' information_schema.psm_routines'
. ' t0, information_schema.schemata t1'
. ' WHERE t0.schema_pk=t1.schema_pk'
. ' AND "routine_kind"=\'FUNCTION\''
. ' AND "schema_name" = current_schema';
case 'procedures':
return 'SELECT "routine_name" FROM'
. ' information_schema.psm_routines'
. ' t0, information_schema.schemata t1'
. ' WHERE t0.schema_pk=t1.schema_pk'
. ' AND "routine_kind"=\'PROCEDURE\''
. ' AND "schema_name" = current_schema';
default:
return null;
}
}
 
// }}}
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/odbc.php
Новый файл
0,0 → 1,883
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's odbc extension
* for interacting with databases via ODBC connections
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Stig Bakken <ssb@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: odbc.php,v 1.81 2007/07/06 05:19:21 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's odbc extension
* for interacting with databases via ODBC connections
*
* These methods overload the ones declared in DB_common.
*
* More info on ODBC errors could be found here:
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp
*
* @category Database
* @package DB
* @author Stig Bakken <ssb@php.net>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
*/
class DB_odbc extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'odbc';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'sql92';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* NOTE: The feature set of the following drivers are different than
* the default:
* + solid: 'transactions' = true
* + navision: 'limit' = false
*
* @var array
*/
var $features = array(
'limit' => 'emulate',
'new_link' => false,
'numrows' => true,
'pconnect' => true,
'prepare' => false,
'ssl' => false,
'transactions' => false,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
'01004' => DB_ERROR_TRUNCATED,
'07001' => DB_ERROR_MISMATCH,
'21S01' => DB_ERROR_VALUE_COUNT_ON_ROW,
'21S02' => DB_ERROR_MISMATCH,
'22001' => DB_ERROR_INVALID,
'22003' => DB_ERROR_INVALID_NUMBER,
'22005' => DB_ERROR_INVALID_NUMBER,
'22008' => DB_ERROR_INVALID_DATE,
'22012' => DB_ERROR_DIVZERO,
'23000' => DB_ERROR_CONSTRAINT,
'23502' => DB_ERROR_CONSTRAINT_NOT_NULL,
'23503' => DB_ERROR_CONSTRAINT,
'23504' => DB_ERROR_CONSTRAINT,
'23505' => DB_ERROR_CONSTRAINT,
'24000' => DB_ERROR_INVALID,
'34000' => DB_ERROR_INVALID,
'37000' => DB_ERROR_SYNTAX,
'42000' => DB_ERROR_SYNTAX,
'42601' => DB_ERROR_SYNTAX,
'IM001' => DB_ERROR_UNSUPPORTED,
'S0000' => DB_ERROR_NOSUCHTABLE,
'S0001' => DB_ERROR_ALREADY_EXISTS,
'S0002' => DB_ERROR_NOSUCHTABLE,
'S0011' => DB_ERROR_ALREADY_EXISTS,
'S0012' => DB_ERROR_NOT_FOUND,
'S0021' => DB_ERROR_ALREADY_EXISTS,
'S0022' => DB_ERROR_NOSUCHFIELD,
'S1009' => DB_ERROR_INVALID,
'S1090' => DB_ERROR_INVALID,
'S1C00' => DB_ERROR_NOT_CAPABLE,
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* The number of rows affected by a data manipulation query
* @var integer
* @access private
*/
var $affected = 0;
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_odbc()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* PEAR DB's odbc driver supports the following extra DSN options:
* + cursor The type of cursor to be used for this connection.
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('odbc')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
switch ($this->dbsyntax) {
case 'access':
case 'db2':
case 'solid':
$this->features['transactions'] = true;
break;
case 'navision':
$this->features['limit'] = false;
}
 
/*
* This is hear for backwards compatibility. Should have been using
* 'database' all along, but prior to 1.6.0RC3 'hostspec' was used.
*/
if ($dsn['database']) {
$odbcdsn = $dsn['database'];
} elseif ($dsn['hostspec']) {
$odbcdsn = $dsn['hostspec'];
} else {
$odbcdsn = 'localhost';
}
 
$connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
 
if (empty($dsn['cursor'])) {
$this->connection = @$connect_function($odbcdsn, $dsn['username'],
$dsn['password']);
} else {
$this->connection = @$connect_function($odbcdsn, $dsn['username'],
$dsn['password'],
$dsn['cursor']);
}
 
if (!is_resource($this->connection)) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$this->errorNative());
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$err = @odbc_close($this->connection);
$this->connection = null;
return $err;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$this->last_query = $query;
$query = $this->modifyQuery($query);
$result = @odbc_exec($this->connection, $query);
if (!$result) {
return $this->odbcRaiseError(); // XXX ERRORMSG
}
// Determine which queries that should return data, and which
// should return an error code only.
if ($this->_checkManip($query)) {
$this->affected = $result; // For affectedRows()
return DB_OK;
}
$this->affected = 0;
return $result;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal odbc result pointer to the next available result
*
* @param a valid fbsql result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return @odbc_next_result($result);
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
$arr = array();
if ($rownum !== null) {
$rownum++; // ODBC first row is 1
if (version_compare(phpversion(), '4.2.0', 'ge')) {
$cols = @odbc_fetch_into($result, $arr, $rownum);
} else {
$cols = @odbc_fetch_into($result, $rownum, $arr);
}
} else {
$cols = @odbc_fetch_into($result, $arr);
}
if (!$cols) {
return null;
}
if ($fetchmode !== DB_FETCHMODE_ORDERED) {
for ($i = 0; $i < count($arr); $i++) {
$colName = @odbc_field_name($result, $i+1);
$a[$colName] = $arr[$i];
}
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$a = array_change_key_case($a, CASE_LOWER);
}
$arr = $a;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? odbc_free_result($result) : false;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @odbc_num_fields($result);
if (!$cols) {
return $this->odbcRaiseError();
}
return $cols;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if (empty($this->affected)) { // In case of SELECT stms
return 0;
}
$nrows = @odbc_num_rows($this->affected);
if ($nrows == -1) {
return $this->odbcRaiseError();
}
return $nrows;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* Not all ODBC drivers support this functionality. If they don't
* a DB_Error object for DB_ERROR_UNSUPPORTED is returned.
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$nrows = @odbc_num_rows($result);
if ($nrows == -1) {
return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED);
}
if ($nrows === false) {
return $this->odbcRaiseError();
}
return $nrows;
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* Quotes a string so it can be safely used as a table or column name
*
* Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked
* "Use ANSI quoted identifiers" when setting up the ODBC data source.
*
* @param string $str identifier name to be quoted
*
* @return string quoted identifier string
*
* @see DB_common::quoteIdentifier()
* @since Method available since Release 1.6.0
*/
function quoteIdentifier($str)
{
switch ($this->dsn['dbsyntax']) {
case 'access':
return '[' . $str . ']';
case 'mssql':
case 'sybase':
return '[' . str_replace(']', ']]', $str) . ']';
case 'mysql':
case 'mysqli':
return '`' . $str . '`';
default:
return '"' . str_replace('"', '""', $str) . '"';
}
}
 
// }}}
// {{{ quote()
 
/**
* @deprecated Deprecated in release 1.6.0
* @internal
*/
function quote($str)
{
return $this->quoteSmart($str);
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_odbc::createSequence(), DB_odbc::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
$repeat = 0;
do {
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("update ${seqname} set id = id + 1");
$this->popErrorHandling();
if ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE) {
$repeat = 1;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->createSequence($seq_name);
$this->popErrorHandling();
if (DB::isError($result)) {
return $this->raiseError($result);
}
$result = $this->query("insert into ${seqname} (id) values(0)");
} else {
$repeat = 0;
}
} while ($repeat);
 
if (DB::isError($result)) {
return $this->raiseError($result);
}
 
$result = $this->query("select id from ${seqname}");
if (DB::isError($result)) {
return $result;
}
 
$row = $result->fetchRow(DB_FETCHMODE_ORDERED);
if (DB::isError($row || !$row)) {
return $row;
}
 
return $row[0];
}
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_odbc::nextID(), DB_odbc::dropSequence()
*/
function createSequence($seq_name)
{
return $this->query('CREATE TABLE '
. $this->getSequenceName($seq_name)
. ' (id integer NOT NULL,'
. ' PRIMARY KEY(id))');
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_odbc::nextID(), DB_odbc::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int DB_OK on success. A DB_Error object if the driver
* doesn't support auto-committing transactions.
*/
function autoCommit($onoff = false)
{
if (!@odbc_autocommit($this->connection, $onoff)) {
return $this->odbcRaiseError();
}
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function commit()
{
if (!@odbc_commit($this->connection)) {
return $this->odbcRaiseError();
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function rollback()
{
if (!@odbc_rollback($this->connection)) {
return $this->odbcRaiseError();
}
return DB_OK;
}
 
// }}}
// {{{ odbcRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_odbc::errorNative(), DB_common::errorCode()
*/
function odbcRaiseError($errno = null)
{
if ($errno === null) {
switch ($this->dbsyntax) {
case 'access':
if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
$this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD;
} else {
// Doing this in case mode changes during runtime.
$this->errorcode_map['07001'] = DB_ERROR_MISMATCH;
}
 
$native_code = odbc_error($this->connection);
 
// S1000 is for "General Error." Let's be more specific.
if ($native_code == 'S1000') {
$errormsg = odbc_errormsg($this->connection);
static $error_regexps;
if (!isset($error_regexps)) {
$error_regexps = array(
'/includes related records.$/i' => DB_ERROR_CONSTRAINT,
'/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL,
);
}
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $errormsg)) {
return $this->raiseError($code,
null, null, null,
$native_code . ' ' . $errormsg);
}
}
$errno = DB_ERROR;
} else {
$errno = $this->errorCode($native_code);
}
break;
default:
$errno = $this->errorCode(odbc_error($this->connection));
}
}
return $this->raiseError($errno, null, null, null,
$this->errorNative());
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error code and message produced by the last query
*
* @return string the DBMS' error code and message
*/
function errorNative()
{
if (!is_resource($this->connection)) {
return @odbc_error() . ' ' . @odbc_errormsg();
}
return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.7.0
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @odbc_exec($this->connection, "SELECT * FROM $result");
if (!$id) {
return $this->odbcRaiseError();
}
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @odbc_num_fields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$col = $i + 1;
$res[$i] = array(
'table' => $got_string ? $case_func($result) : '',
'name' => $case_func(@odbc_field_name($id, $col)),
'type' => @odbc_field_type($id, $col),
'len' => @odbc_field_len($id, $col),
'flags' => '',
);
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@odbc_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtains the query string needed for listing a given type of objects
*
* Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com.
*
* @param string $type the kind of objects you want to retrieve
*
* @return string the list of objects requested
*
* @access protected
* @see DB_common::getListOf()
* @since Method available since Release 1.7.0
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'databases':
if (!function_exists('odbc_data_source')) {
return null;
}
$res = @odbc_data_source($this->connection, SQL_FETCH_FIRST);
if (is_array($res)) {
$out = array($res['server']);
while($res = @odbc_data_source($this->connection,
SQL_FETCH_NEXT))
{
$out[] = $res['server'];
}
return $out;
} else {
return $this->odbcRaiseError();
}
break;
case 'tables':
case 'schema.tables':
$keep = 'TABLE';
break;
case 'views':
$keep = 'VIEW';
break;
default:
return null;
}
 
/*
* Removing non-conforming items in the while loop rather than
* in the odbc_tables() call because some backends choke on this:
* odbc_tables($this->connection, '', '', '', 'TABLE')
*/
$res = @odbc_tables($this->connection);
if (!$res) {
return $this->odbcRaiseError();
}
$out = array();
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] != $keep) {
continue;
}
if ($type == 'schema.tables') {
$out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME'];
} else {
$out[] = $row['TABLE_NAME'];
}
}
return $out;
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/lib/DB/msql.php
Новый файл
0,0 → 1,831
<?php
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* The PEAR DB driver for PHP's msql extension
* for interacting with Mini SQL databases
*
* PHP's mSQL extension did weird things with NULL values prior to PHP
* 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
* those versions.
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: msql.php,v 1.64 2007/09/21 13:40:41 aharvey Exp $
* @link http://pear.php.net/package/DB
*/
 
/**
* Obtain the DB_common class so it can be extended from
*/
require_once 'DB/common.php';
 
/**
* The methods PEAR DB uses to interact with PHP's msql extension
* for interacting with Mini SQL databases
*
* These methods overload the ones declared in DB_common.
*
* PHP's mSQL extension did weird things with NULL values prior to PHP
* 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
* those versions.
*
* @category Database
* @package DB
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.13
* @link http://pear.php.net/package/DB
* @since Class not functional until Release 1.7.0
*/
class DB_msql extends DB_common
{
// {{{ properties
 
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
var $phptype = 'msql';
 
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
var $dbsyntax = 'msql';
 
/**
* The capabilities of this DB implementation
*
* The 'new_link' element contains the PHP version that first provided
* new_link support for this DBMS. Contains false if it's unsupported.
*
* Meaning of the 'limit' element:
* + 'emulate' = emulate with fetch row by number
* + 'alter' = alter the query
* + false = skip rows
*
* @var array
*/
var $features = array(
'limit' => 'emulate',
'new_link' => false,
'numrows' => true,
'pconnect' => true,
'prepare' => false,
'ssl' => false,
'transactions' => false,
);
 
/**
* A mapping of native error codes to DB error codes
* @var array
*/
var $errorcode_map = array(
);
 
/**
* The raw database connection created by PHP
* @var resource
*/
var $connection;
 
/**
* The DSN information for connecting to a database
* @var array
*/
var $dsn = array();
 
 
/**
* The query result resource created by PHP
*
* Used to make affectedRows() work. Only contains the result for
* data manipulation queries. Contains false for other queries.
*
* @var resource
* @access private
*/
var $_result;
 
 
// }}}
// {{{ constructor
 
/**
* This constructor calls <kbd>$this->DB_common()</kbd>
*
* @return void
*/
function DB_msql()
{
$this->DB_common();
}
 
// }}}
// {{{ connect()
 
/**
* Connect to the database server, log in and open the database
*
* Don't call this method directly. Use DB::connect() instead.
*
* Example of how to connect:
* <code>
* require_once 'DB.php';
*
* // $dsn = 'msql://hostname/dbname'; // use a TCP connection
* $dsn = 'msql:///dbname'; // use a socket
* $options = array(
* 'portability' => DB_PORTABILITY_ALL,
* );
*
* $db = DB::connect($dsn, $options);
* if (PEAR::isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
function connect($dsn, $persistent = false)
{
if (!PEAR::loadExtension('msql')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
 
$this->dsn = $dsn;
if ($dsn['dbsyntax']) {
$this->dbsyntax = $dsn['dbsyntax'];
}
 
$params = array();
if ($dsn['hostspec']) {
$params[] = $dsn['port']
? $dsn['hostspec'] . ',' . $dsn['port']
: $dsn['hostspec'];
}
 
$connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
 
$ini = ini_get('track_errors');
$php_errormsg = '';
if ($ini) {
$this->connection = @call_user_func_array($connect_function,
$params);
} else {
@ini_set('track_errors', 1);
$this->connection = @call_user_func_array($connect_function,
$params);
@ini_set('track_errors', $ini);
}
 
if (!$this->connection) {
if (($err = @msql_error()) != '') {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$err);
} else {
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
null, null, null,
$php_errormsg);
}
}
 
if (!@msql_select_db($dsn['database'], $this->connection)) {
return $this->msqlRaiseError();
}
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
function disconnect()
{
$ret = @msql_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Sends a query to the database server
*
* @param string the SQL query string
*
* @return mixed + a PHP result resrouce for successful SELECT queries
* + the DB_OK constant for other successful queries
* + a DB_Error object on failure
*/
function simpleQuery($query)
{
$this->last_query = $query;
$query = $this->modifyQuery($query);
$result = @msql_query($query, $this->connection);
if (!$result) {
return $this->msqlRaiseError();
}
// Determine which queries that should return data, and which
// should return an error code only.
if ($this->_checkManip($query)) {
$this->_result = $result;
return DB_OK;
} else {
$this->_result = false;
return $result;
}
}
 
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal msql result pointer to the next available result
*
* @param a valid fbsql result resource
*
* @access public
*
* @return true if a result is available otherwise return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Places a row from the result set into the given array
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* This method is not meant to be called directly. Use
* DB_result::fetchInto() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* PHP's mSQL extension did weird things with NULL values prior to PHP
* 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds
* those versions.
*
* @param resource $result the query result resource
* @param array $arr the referenced array to put the data in
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch (0 = first row)
*
* @return mixed DB_OK on success, NULL when the end of a result set is
* reached or on failure
*
* @see DB_result::fetchInto()
*/
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
{
if ($rownum !== null) {
if (!@msql_data_seek($result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @msql_fetch_array($result, MSQL_ASSOC);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @msql_fetch_row($result);
}
if (!$arr) {
return null;
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Deletes the result set and frees the memory occupied by the result set
*
* This method is not meant to be called directly. Use
* DB_result::free() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return bool TRUE on success, FALSE if $result is invalid
*
* @see DB_result::free()
*/
function freeResult($result)
{
return is_resource($result) ? msql_free_result($result) : false;
}
 
// }}}
// {{{ numCols()
 
/**
* Gets the number of columns in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numCols() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
function numCols($result)
{
$cols = @msql_num_fields($result);
if (!$cols) {
return $this->msqlRaiseError();
}
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Gets the number of rows in a result set
*
* This method is not meant to be called directly. Use
* DB_result::numRows() instead. It can't be declared "protected"
* because DB_result is a separate object.
*
* @param resource $result PHP's query result resource
*
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
function numRows($result)
{
$rows = @msql_num_rows($result);
if ($rows === false) {
return $this->msqlRaiseError();
}
return $rows;
}
 
// }}}
// {{{ affected()
 
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int the number of rows. A DB_Error object on failure.
*/
function affectedRows()
{
if (!$this->_result) {
return 0;
}
return msql_affected_rows($this->_result);
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence.
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_msql::createSequence(), DB_msql::dropSequence()
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
$repeat = false;
do {
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("SELECT _seq FROM ${seqname}");
$this->popErrorHandling();
if ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE) {
$repeat = true;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->createSequence($seq_name);
$this->popErrorHandling();
if (DB::isError($result)) {
return $this->raiseError($result);
}
} else {
$repeat = false;
}
} while ($repeat);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
$result->free();
return $arr[0];
}
 
// }}}
// {{{ createSequence()
 
/**
* Creates a new sequence
*
* Also creates a new table to associate the sequence with. Uses
* a separate table to ensure portability with other drivers.
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_msql::nextID(), DB_msql::dropSequence()
*/
function createSequence($seq_name)
{
$seqname = $this->getSequenceName($seq_name);
$res = $this->query('CREATE TABLE ' . $seqname
. ' (id INTEGER NOT NULL)');
if (DB::isError($res)) {
return $res;
}
$res = $this->query("CREATE SEQUENCE ON ${seqname}");
return $res;
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. A DB_Error object on failure.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_msql::nextID(), DB_msql::createSequence()
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* mSQL does not support delimited identifiers
*
* @param string $str the identifier name to be quoted
*
* @return object a DB_Error object
*
* @see DB_common::quoteIdentifier()
* @since Method available since Release 1.7.0
*/
function quoteIdentifier($str)
{
return $this->raiseError(DB_ERROR_UNSUPPORTED);
}
 
// }}}
// {{{ quoteFloat()
 
/**
* Formats a float value for use within a query in a locale-independent
* manner.
*
* @param float the float value to be quoted.
* @return string the quoted string.
* @see DB_common::quoteSmart()
* @since Method available since release 1.7.8.
*/
function quoteFloat($float) {
return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
}
// }}}
// {{{ escapeSimple()
 
/**
* Escapes a string according to the current DBMS's standards
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @see DB_common::quoteSmart()
* @since Method available since Release 1.7.0
*/
function escapeSimple($str)
{
return addslashes($str);
}
 
// }}}
// {{{ msqlRaiseError()
 
/**
* Produces a DB_Error object regarding the current problem
*
* @param int $errno if the error is being manually raised pass a
* DB_ERROR* constant here. If this isn't passed
* the error information gathered from the DBMS.
*
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_msql::errorNative(), DB_msql::errorCode()
*/
function msqlRaiseError($errno = null)
{
$native = $this->errorNative();
if ($errno === null) {
$errno = $this->errorCode($native);
}
return $this->raiseError($errno, null, null, null, $native);
}
 
// }}}
// {{{ errorNative()
 
/**
* Gets the DBMS' native error message produced by the last query
*
* @return string the DBMS' error message
*/
function errorNative()
{
return @msql_error();
}
 
// }}}
// {{{ errorCode()
 
/**
* Determines PEAR::DB error code from the database's text error message
*
* @param string $errormsg the error message returned from the database
*
* @return integer the error number from a DB_ERROR* constant
*/
function errorCode($errormsg)
{
static $error_regexps;
// PHP 5.2+ prepends the function name to $php_errormsg, so we need
// this hack to work around it, per bug #9599.
$errormsg = preg_replace('/^msql[a-z_]+\(\): /', '', $errormsg);
 
if (!isset($error_regexps)) {
$error_regexps = array(
'/^Access to database denied/i'
=> DB_ERROR_ACCESS_VIOLATION,
'/^Bad index name/i'
=> DB_ERROR_ALREADY_EXISTS,
'/^Bad order field/i'
=> DB_ERROR_SYNTAX,
'/^Bad type for comparison/i'
=> DB_ERROR_SYNTAX,
'/^Can\'t perform LIKE on/i'
=> DB_ERROR_SYNTAX,
'/^Can\'t use TEXT fields in LIKE comparison/i'
=> DB_ERROR_SYNTAX,
'/^Couldn\'t create temporary table/i'
=> DB_ERROR_CANNOT_CREATE,
'/^Error creating table file/i'
=> DB_ERROR_CANNOT_CREATE,
'/^Field .* cannot be null$/i'
=> DB_ERROR_CONSTRAINT_NOT_NULL,
'/^Index (field|condition) .* cannot be null$/i'
=> DB_ERROR_SYNTAX,
'/^Invalid date format/i'
=> DB_ERROR_INVALID_DATE,
'/^Invalid time format/i'
=> DB_ERROR_INVALID,
'/^Literal value for .* is wrong type$/i'
=> DB_ERROR_INVALID_NUMBER,
'/^No Database Selected/i'
=> DB_ERROR_NODBSELECTED,
'/^No value specified for field/i'
=> DB_ERROR_VALUE_COUNT_ON_ROW,
'/^Non unique value for unique index/i'
=> DB_ERROR_CONSTRAINT,
'/^Out of memory for temporary table/i'
=> DB_ERROR_CANNOT_CREATE,
'/^Permission denied/i'
=> DB_ERROR_ACCESS_VIOLATION,
'/^Reference to un-selected table/i'
=> DB_ERROR_SYNTAX,
'/^syntax error/i'
=> DB_ERROR_SYNTAX,
'/^Table .* exists$/i'
=> DB_ERROR_ALREADY_EXISTS,
'/^Unknown database/i'
=> DB_ERROR_NOSUCHDB,
'/^Unknown field/i'
=> DB_ERROR_NOSUCHFIELD,
'/^Unknown (index|system variable)/i'
=> DB_ERROR_NOT_FOUND,
'/^Unknown table/i'
=> DB_ERROR_NOSUCHTABLE,
'/^Unqualified field/i'
=> DB_ERROR_SYNTAX,
);
}
 
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $errormsg)) {
return $code;
}
}
return DB_ERROR;
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A DB_Error object on failure.
*
* @see DB_common::setOption()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @msql_query("SELECT * FROM $result",
$this->connection);
$got_string = true;
} elseif (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @msql_num_fields($id);
$res = array();
 
if ($mode) {
$res['num_fields'] = $count;
}
 
for ($i = 0; $i < $count; $i++) {
$tmp = @msql_fetch_field($id);
 
$flags = '';
if ($tmp->not_null) {
$flags .= 'not_null ';
}
if ($tmp->unique) {
$flags .= 'unique_key ';
}
$flags = trim($flags);
 
$res[$i] = array(
'table' => $case_func($tmp->table),
'name' => $case_func($tmp->name),
'type' => $tmp->type,
'len' => msql_field_len($id, $i),
'flags' => $flags,
);
 
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@msql_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Obtain a list of a given type of objects
*
* @param string $type the kind of objects you want to retrieve
*
* @return array the array containing the list of objects requested
*
* @access protected
* @see DB_common::getListOf()
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'databases':
$id = @msql_list_dbs($this->connection);
break;
case 'tables':
$id = @msql_list_tables($this->dsn['database'],
$this->connection);
break;
default:
return null;
}
if (!$id) {
return $this->msqlRaiseError();
}
$out = array();
while ($row = @msql_fetch_row($id)) {
$out[] = $row[0];
}
return $out;
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>