[DATABASE] Switch from PEAR DB to MDB2

This commit is contained in:
Alexei Sorokin 2020-09-14 22:46:29 +03:00
parent 96f1cc1a5c
commit fde929b151
97 changed files with 48799 additions and 16730 deletions

View File

@ -173,10 +173,6 @@ The ones that you may want to set are listed below for clarity.
Note that the real name of your database should go in there, not literally
'yourdbname'.
* `db_driver`(enum['DB','MDB2'], default null): You can try changing this to
'MDB2' to use the other driver type for DB_DataObject, but note that it
breaks the OpenID libraries, which only support PEAR::DB.
* `type` (enum["mysql", "pgsql"], default 'mysql'): Used for certain
database-specific optimization code. Assumes mysql if not set. "mysql"
covers MariaDB, Oracle MySQL, mysqli or otherwise.

File diff suppressed because it is too large Load Diff

View File

@ -701,7 +701,13 @@ class DB_DataObject_Generator extends DB_DataObject
die($res->getMessage());
}
while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
if ($db_driver == 'DB') {
$fetchmode = DB_FETCHMODE_ASSOC;
} else {
$fetchmode = MDB2_FETCHMODE_ASSOC;
}
while ($row = $res->fetchRow($fetchmode)) {
$treffer = array();
// this only picks up one of these.. see this for why: http://pear.php.net/bugs/bug.php?id=17049
preg_match(
@ -731,7 +737,11 @@ class DB_DataObject_Generator extends DB_DataObject
die($res->getMessage());
}
$text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
if ($db_driver == 'DB') {
$text = $res->fetchRow(DB_FETCHMODE_DEFAULT, 0);
} else {
$text = $res->fetchRow(MDB2_FETCHMODE_DEFAULT, 0);
}
$treffer = array();
// Extract FOREIGN KEYS
preg_match_all(
@ -1558,7 +1568,11 @@ class DB_DataObject_Generator extends DB_DataObject
$options = (new PEAR)->getStaticProperty('DB_DataObject', 'options');
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
$method = $db_driver == 'DB' ? 'getAll' : 'queryAll';
$res = $__DB->$method('DESCRIBE ' . $table, DB_FETCHMODE_ASSOC);
if ($db_driver == 'DB') {
$res = $__DB->$method('DESCRIBE ' . $table, DB_FETCHMODE_ASSOC);
} else {
$res = $__DB->$method('DESCRIBE ' . $table, MDB2_FETCHMODE_ASSOC);
}
$defaults = array();
foreach ($res as $ar) {
// this is initially very dumb... -> and it may mess up..

File diff suppressed because it is too large Load Diff

View File

@ -1,531 +0,0 @@
<?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 version 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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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.9.2
* @link http://pear.php.net/package/DB
*/
class DB_dbase extends DB_common
{
// {{{ properties
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
public $phptype = 'dbase';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
*/
public $errorcode_map = array();
/**
* The raw database connection created by PHP
* @var resource
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $dsn = array();
/**
* A means of emulating result resources
* @var array
*/
public $res_row = array();
/**
* The quantity of results so far
*
* For emulating result resources.
*
* @var integer
*/
public $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
*/
public $types = array(
'C' => 'character',
'D' => 'date',
'L' => 'boolean',
'M' => 'memo',
'N' => 'number',
);
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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 ((new PEAR)->isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int|object
*/
public 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
*/
public function disconnect()
{
$ret = @dbase_close($this->connection);
$this->connection = null;
return $ret;
}
// }}}
// {{{ &query()
public 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()
*/
public 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()
*/
public 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 $foo
* @return int the number of columns. A DB_Error object on failure.
*
* @see DB_result::numCols()
*/
public 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 $foo
* @return int the number of rows. A DB_Error object on failure.
*
* @see DB_result::numRows()
*/
public 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.
*/
public 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|object
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.7.0
*/
public 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:
*/

View File

@ -1,795 +0,0 @@
<?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 version 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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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.9.2
* @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
*/
public $phptype = 'fbsql';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
*/
public $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
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $dsn = array();
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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|object
*/
public 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()
/**
* 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()
*/
public 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)
);
}
// }}}
// {{{ simpleQuery()
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
public function disconnect()
{
$ret = @fbsql_close($this->connection);
$this->connection = null;
return $ret;
}
// }}}
// {{{ nextResult()
/**
* 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
*/
public 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;
}
// }}}
// {{{ fetchInto()
/**
* 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
*/
public function nextResult($result)
{
return @fbsql_next_result($result);
}
// }}}
// {{{ freeResult()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ autoCommit()
/**
* 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()
*/
public function freeResult($result)
{
return is_resource($result) ? fbsql_free_result($result) : false;
}
// }}}
// {{{ commit()
/**
* 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.
*/
public function autoCommit($onoff = false)
{
if ($onoff) {
$this->query("SET COMMIT TRUE");
} else {
$this->query("SET COMMIT FALSE");
}
return null;
}
// }}}
// {{{ rollback()
/**
* Commits the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
public function commit()
{
@fbsql_commit($this->connection);
return 0;
}
// }}}
// {{{ numCols()
/**
* Reverts the current transaction
*
* @return int DB_OK on success. A DB_Error object on failure.
*/
public function rollback()
{
@fbsql_rollback($this->connection);
return 0;
}
// }}}
// {{{ numRows()
/**
* 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|object
*
* @see DB_result::numCols()
*/
public function numCols($result)
{
$cols = @fbsql_num_fields($result);
if (!$cols) {
return $this->fbsqlRaiseError();
}
return $cols;
}
// }}}
// {{{ affectedRows()
/**
* 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|object
*
* @see DB_result::numRows()
*/
public function numRows($result)
{
$rows = @fbsql_num_rows($result);
if ($rows === null) {
return $this->fbsqlRaiseError();
}
return $rows;
}
// }}}
// {{{ nextId()
/**
* 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.
*/
public function affectedRows()
{
if ($this->_last_query_manip) {
$result = @fbsql_affected_rows($this->connection);
} else {
$result = 0;
}
return $result;
}
/**
* 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|object
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_fbsql::createSequence(), DB_fbsql::dropSequence()
*/
public 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];
}
// }}}
// {{{ dropSequence()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ modifyLimitQuery()
/**
* 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()
*/
public function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)
. ' RESTRICT');
}
// }}}
// {{{ quoteBoolean()
/**
* 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
*/
public 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
);
}
}
// }}}
// {{{ quoteFloat()
/**
* 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.
*/
public function quoteBoolean($boolean)
{
return $boolean ? 'TRUE' : 'FALSE';
}
// }}}
// {{{ fbsqlRaiseError()
/**
* 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.
*/
public function quoteFloat($float)
{
return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
}
// }}}
// {{{ errorNative()
/**
* Gets the DBMS' native error code produced by the last query
*
* @return int the DBMS' error code
*/
public 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|object
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
*/
public 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()
*/
public 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:
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,686 +0,0 @@
<?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 version 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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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.9.2
* @link http://pear.php.net/package/DB
*/
class DB_ifx extends DB_common
{
// {{{ properties
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
public $phptype = 'ifx';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
*/
public $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
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $dsn = array();
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
public $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
*/
public $transaction_opcount = 0;
/**
* The number of rows affected by a data manipulation query
* @var integer
* @access private
*/
public $affected = 0;
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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|object
*/
public 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()
/**
* 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()
*/
public function ifxRaiseError($errno = null)
{
if ($errno === null) {
$errno = $this->errorCode(ifx_error());
}
return $this->raiseError(
$errno,
null,
null,
null,
$this->errorNative()
);
}
// }}}
// {{{ simpleQuery()
/**
* 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.
*/
public function errorCode($nativecode)
{
if (preg_match('/SQLCODE=(.*)]/', $nativecode, $match)) {
$code = $match[1];
if (isset($this->errorcode_map[$code])) {
return $this->errorcode_map[$code];
}
}
return DB_ERROR;
}
// }}}
// {{{ nextResult()
/**
* Gets the DBMS' native error code and message produced by the last query
*
* @return string the DBMS' error code and message
*/
public function errorNative()
{
return @ifx_error() . ' ' . @ifx_errormsg();
}
// }}}
// {{{ affectedRows()
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
public function disconnect()
{
$ret = @ifx_close($this->connection);
$this->connection = null;
return $ret;
}
// }}}
// {{{ fetchInto()
/**
* 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
*/
public 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;
}
// }}}
// {{{ numCols()
/**
* 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
*/
public function nextResult($result)
{
return false;
}
// }}}
// {{{ freeResult()
/**
* 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.
*/
public function affectedRows()
{
if ($this->_last_query_manip) {
return $this->affected;
} else {
return 0;
}
}
// }}}
// {{{ autoCommit()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ commit()
/**
* 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|object
*
* @see DB_result::numCols()
*/
public function numCols($result)
{
if (!$cols = @ifx_num_fields($result)) {
return $this->ifxRaiseError();
}
return $cols;
}
// }}}
// {{{ rollback()
/**
* 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()
*/
public function freeResult($result)
{
return is_resource($result) ? ifx_free_result($result) : false;
}
// }}}
// {{{ ifxRaiseError()
/**
* 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.
*/
public 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;
}
// }}}
// {{{ errorNative()
/**
* Commits the current transaction
*
* @return int|object
*/
public 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;
}
// }}}
// {{{ errorCode()
/**
* Reverts the current transaction
*
* @return int|object
*/
public 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;
}
// }}}
// {{{ 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|object
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.6.0
*/
public 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()
*/
public 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:
*/

View File

@ -1,845 +0,0 @@
<?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 version 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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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.9.2
* @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
*/
public $phptype = 'msql';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
*/
public $errorcode_map = array();
/**
* The raw database connection created by PHP
* @var resource
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $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
*/
public $_result;
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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 ((new PEAR)->isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int|object
*/
public 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()
/**
* 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()
*/
public function msqlRaiseError($errno = null)
{
$native = $this->errorNative();
if ($errno === null) {
$errno = $this->errorCode($native);
}
return $this->raiseError($errno, null, null, null, $native);
}
// }}}
// {{{ simpleQuery()
/**
* Gets the DBMS' native error message produced by the last query
*
* @return string the DBMS' error message
*/
public function errorNative()
{
return @msql_error();
}
// }}}
// {{{ nextResult()
/**
* 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
*/
public 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;
}
// }}}
// {{{ fetchInto()
/**
* Disconnects from the database server
*
* @return bool TRUE on success, FALSE on failure
*/
public function disconnect()
{
$ret = @msql_close($this->connection);
$this->connection = null;
return $ret;
}
// }}}
// {{{ freeResult()
/**
* 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
*/
public 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;
}
}
// }}}
// {{{ numCols()
/**
* 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
*/
public function nextResult($result)
{
return false;
}
// }}}
// {{{ numRows()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ affected()
/**
* 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()
*/
public function freeResult($result)
{
return is_resource($result) ? msql_free_result($result) : false;
}
// }}}
// {{{ nextId()
/**
* 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|object
*
* @see DB_result::numCols()
*/
public function numCols($result)
{
$cols = @msql_num_fields($result);
if (!$cols) {
return $this->msqlRaiseError();
}
return $cols;
}
// }}}
// {{{ createSequence()
/**
* 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|object
*
* @see DB_result::numRows()
*/
public function numRows($result)
{
$rows = @msql_num_rows($result);
if ($rows === false) {
return $this->msqlRaiseError();
}
return $rows;
}
// }}}
// {{{ dropSequence()
/**
* 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.
*/
public function affectedRows()
{
if (!$this->_result) {
return 0;
}
return msql_affected_rows($this->_result);
}
// }}}
// {{{ quoteIdentifier()
/**
* 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|object
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_msql::createSequence(), DB_msql::dropSequence()
*/
public 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];
}
// }}}
// {{{ quoteFloat()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ escapeSimple()
/**
* 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()
*/
public function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
// }}}
// {{{ msqlRaiseError()
/**
* 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
*/
public function quoteIdentifier($str)
{
return $this->raiseError(DB_ERROR_UNSUPPORTED);
}
// }}}
// {{{ errorNative()
/**
* 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.
*/
public function quoteFloat($float)
{
return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
}
// }}}
// {{{ errorCode()
/**
* 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
*/
public function escapeSimple($str)
{
return addslashes($str);
}
// }}}
// {{{ 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|object
* A DB_Error object on failure.
*
* @see DB_common::setOption()
*/
public 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|object
*
* @access protected
* @see DB_common::getListOf()
*/
public 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:
*/

View File

@ -1,994 +0,0 @@
<?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 version 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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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.9.2
* @link http://pear.php.net/package/DB
*/
class DB_mssql extends DB_common
{
// {{{ properties
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
public $phptype = 'mssql';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
public $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
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $dsn = array();
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
public $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
*/
public $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
*/
public $_db = null;
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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|object
*/
public 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
*/
public 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
*/
public 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()
/**
* Produces a DB_Error object regarding the current problem
*
* @param null $code
* @return object the DB_Error object
*
* @see DB_common::raiseError(),
* DB_mssql::errorNative(), DB_mssql::errorCode()
*/
public 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"
);
}
// }}}
// {{{ fetchInto()
/**
* Gets the DBMS' native error code produced by the last query
*
* @return int the DBMS' error code
*/
public function errorNative()
{
$res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
if (!$res) {
return DB_ERROR;
}
$row = @mssql_fetch_row($res);
return $row[0];
}
// }}}
// {{{ freeResult()
/**
* 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
* @param string $msg
* @return integer an error number from a DB error constant
* @see errorNative()
*/
public 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;
}
}
// }}}
// {{{ numCols()
/**
* 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
*/
public function nextResult($result)
{
return @mssql_next_result($result);
}
// }}}
// {{{ numRows()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ autoCommit()
/**
* 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()
*/
public function freeResult($result)
{
return is_resource($result) ? mssql_free_result($result) : false;
}
// }}}
// {{{ commit()
/**
* 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|object
*
* @see DB_result::numCols()
*/
public function numCols($result)
{
$cols = @mssql_num_fields($result);
if (!$cols) {
return $this->mssqlRaiseError();
}
return $cols;
}
// }}}
// {{{ rollback()
/**
* 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|object
*
* @see DB_result::numRows()
*/
public function numRows($result)
{
$rows = @mssql_num_rows($result);
if ($rows === false) {
return $this->mssqlRaiseError();
}
return $rows;
}
// }}}
// {{{ affectedRows()
/**
* 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.
*/
public 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;
}
// }}}
// {{{ nextId()
/**
* Commits the current transaction
*
* @return int|object
*/
public 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;
}
/**
* Reverts the current transaction
*
* @return int|object
*/
public 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;
}
// }}}
// {{{ dropSequence()
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int|object
*/
public 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;
}
// }}}
// {{{ escapeSimple()
/**
* 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|object
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_mssql::createSequence(), DB_mssql::dropSequence()
*/
public 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];
}
// }}}
// {{{ quoteIdentifier()
/**
* 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()
*/
public function createSequence($seq_name)
{
return $this->query('CREATE TABLE '
. $this->getSequenceName($seq_name)
. ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
. ' [vapor] [int] NULL)');
}
// }}}
// {{{ mssqlRaiseError()
/**
* 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()
*/
public function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
// }}}
// {{{ errorNative()
/**
* Escapes a string in a manner suitable for SQL Server.
*
* @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
*/
public function escapeSimple($str)
{
return str_replace(
array("'", "\\\r\n", "\\\n"),
array("''", "\\\\\r\n\r\n", "\\\\\n\n"),
$str
);
}
// }}}
// {{{ errorCode()
/**
* 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
*/
public function quoteIdentifier($str)
{
return '[' . str_replace(']', ']]', $str) . ']';
}
// }}}
// {{{ 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|object
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
*/
public 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 array|string
*
* @access private
* @author Joern Barthel <j_barthel@web.de>
*/
public 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>
*/
public 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()
*/
public 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:
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,889 +0,0 @@
<?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 version 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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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.9.2
* @link http://pear.php.net/package/DB
*/
class DB_odbc extends DB_common
{
// {{{ properties
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
public $phptype = 'odbc';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
*/
public $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
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $dsn = array();
/**
* The number of rows affected by a data manipulation query
* @var integer
* @access private
*/
public $affected = 0;
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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|object
*/
public 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()
/**
* Gets the DBMS' native error code and message produced by the last query
*
* @return string the DBMS' error code and message
*/
public function errorNative()
{
if (!is_resource($this->connection)) {
return @odbc_error() . ' ' . @odbc_errormsg();
}
return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
}
// }}}
// {{{ simpleQuery()
/**
* Disconnects from the database server
*
* @return bool|void
*/
public function disconnect()
{
$err = @odbc_close($this->connection);
$this->connection = null;
return $err;
}
// }}}
// {{{ nextResult()
/**
* 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
*/
public 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;
}
// }}}
// {{{ fetchInto()
/**
* 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()
*/
public 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()
);
}
// }}}
// {{{ freeResult()
/**
* 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
*/
public function nextResult($result)
{
return @odbc_next_result($result);
}
// }}}
// {{{ numCols()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ affectedRows()
/**
* 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()
*/
public function freeResult($result)
{
return is_resource($result) ? odbc_free_result($result) : false;
}
// }}}
// {{{ numRows()
/**
* 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|object
*
* @see DB_result::numCols()
*/
public function numCols($result)
{
$cols = @odbc_num_fields($result);
if (!$cols) {
return $this->odbcRaiseError();
}
return $cols;
}
// }}}
// {{{ quoteIdentifier()
/**
* Determines the number of rows affected by a data maniuplation query
*
* 0 is returned for queries that don't manipulate data.
*
* @return int|object
*/
public 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;
}
// }}}
// {{{ nextId()
/**
* 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|object
*
* @see DB_result::numRows()
*/
public 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;
}
/**
* 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
*/
public 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) . '"';
}
}
// }}}
// {{{ dropSequence()
/**
* 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|object
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_odbc::createSequence(), DB_odbc::dropSequence()
*/
public 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];
}
// }}}
// {{{ autoCommit()
/**
* 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()
*/
public function createSequence($seq_name)
{
return $this->query('CREATE TABLE '
. $this->getSequenceName($seq_name)
. ' (id integer NOT NULL,'
. ' PRIMARY KEY(id))');
}
// }}}
// {{{ commit()
/**
* 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()
*/
public function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
// }}}
// {{{ rollback()
/**
* Enables or disables automatic commits
*
* @param bool $onoff true turns it on, false turns it off
*
* @return int|object
* doesn't support auto-committing transactions.
*/
public function autoCommit($onoff = false)
{
if (!@odbc_autocommit($this->connection, $onoff)) {
return $this->odbcRaiseError();
}
return DB_OK;
}
// }}}
// {{{ odbcRaiseError()
/**
* Commits the current transaction
*
* @return int|object
*/
public function commit()
{
if (!@odbc_commit($this->connection)) {
return $this->odbcRaiseError();
}
return DB_OK;
}
// }}}
// {{{ errorNative()
/**
* Reverts the current transaction
*
* @return int|object
*/
public function rollback()
{
if (!@odbc_rollback($this->connection)) {
return $this->odbcRaiseError();
}
return DB_OK;
}
// }}}
// {{{ 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|object
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.7.0
*/
public 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 array|string
*
* @access protected
* @see DB_common::getListOf()
* @since Method available since Release 1.7.0
*/
public 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:
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,978 +0,0 @@
<?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 version 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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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.9.2
* @link http://pear.php.net/package/DB
*/
class DB_sqlite extends DB_common
{
// {{{ properties
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
public $phptype = 'sqlite';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
*/
public $errorcode_map = array();
/**
* The raw database connection created by PHP
* @var resource
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $dsn = array();
/**
* SQLite data types
*
* @link http://www.sqlite.org/datatypes.html
*
* @var array
*/
public $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
*/
public $_lasterror = '';
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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 ((new PEAR)->isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param array $dsn the data source name
* @param bool $persistent should the connection be persistent?
*
* @return int|object
*/
public 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()
/**
* 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()
*/
public 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);
}
// }}}
// {{{ simpleQuery()
/**
* 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
*/
public function errorNative()
{
return $this->_lasterror;
}
// }}}
// {{{ nextResult()
/**
* 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
*/
public 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,
'/no column named/' => 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;
}
// }}}
// {{{ fetchInto()
/**
* Disconnects from the database server
*
* @return bool|void
*/
public function disconnect()
{
$ret = @sqlite_close($this->connection);
$this->connection = null;
return $ret;
}
// }}}
// {{{ freeResult()
/**
* 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
*/
public 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;
}
// }}}
// {{{ numCols()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ 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|object
*
* @see DB_result::numRows()
*/
public function numRows($result)
{
$rows = @sqlite_num_rows($result);
if ($rows === null) {
return $this->sqliteRaiseError();
}
return $rows;
}
// }}}
// {{{ affected()
/**
* 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
*/
public function nextResult($result)
{
return false;
}
// }}}
// {{{ dropSequence()
/**
* 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()
*/
public 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;
}
/**
* 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()
*/
public function freeResult(&$result)
{
// XXX No native free?
if (!is_resource($result)) {
return false;
}
$result = null;
return true;
}
// }}}
// {{{ nextId()
/**
* 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|object
*
* @see DB_result::numCols()
*/
public function numCols($result)
{
$cols = @sqlite_num_fields($result);
if (!$cols) {
return $this->sqliteRaiseError();
}
return $cols;
}
// }}}
// {{{ getDbFileStats()
/**
* 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.
*/
public function affectedRows()
{
return @sqlite_changes($this->connection);
}
// }}}
// {{{ escapeSimple()
/**
* 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()
*/
public function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
// }}}
// {{{ modifyLimitQuery()
/**
* 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|object
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_sqlite::createSequence(), DB_sqlite::dropSequence()
*/
public 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);
}
// }}}
// {{{ modifyQuery()
/**
* 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()
*/
public 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);
//}
}
// }}}
// {{{ sqliteRaiseError()
/**
* 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
*/
public 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;
}
// }}}
// {{{ errorNative()
/**
* 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()
*/
public function escapeSimple($str)
{
return @sqlite_escape_string($str);
}
// }}}
// {{{ errorCode()
/**
* 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
*/
public function modifyLimitQuery($query, $from, $count, $params = array())
{
return "$query LIMIT $count OFFSET $from";
}
// }}}
// {{{ 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|object
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.7.0
*/
public 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 (strtoupper($type) == 'INTEGER') {
$flags .= 'auto_increment ';
}
}
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()
*/
public 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:
*/

View File

@ -1,548 +0,0 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Provides an object interface to a table row
*
* PHP version 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 <stig@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB class so it can be extended from
*/
require_once 'DB.php';
/**
* Provides an object interface to a table row
*
* It lets you add, delete and change rows using objects rather than SQL
* statements.
*
* @category Database
* @package DB
* @author Stig Bakken <stig@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_storage extends PEAR
{
// {{{ properties
/** the name of the table (or view, if the backend database supports
* updates in views) we hold data from */
public $_table = null;
/** which column(s) in the table contains primary keys, can be a
* string for single-column primary keys, or an array of strings
* for multiple-column primary keys */
public $_keycolumn = null;
/** DB connection handle used for all transactions */
public $_dbh = null;
/** an assoc with the names of database fields stored as properties
* in this object */
public $_properties = array();
/** an assoc with the names of the properties in this object that
* have been changed since they were fetched from the database */
public $_changes = array();
/** flag that decides if data in this object can be changed.
* objects that don't have their table's key column in their
* property lists will be flagged as read-only. */
public $_readonly = false;
/** function or method that implements a validator for fields that
* are set, this validator function returns true if the field is
* valid, false if not */
public $_validator = null;
// }}}
// {{{ constructor
/**
* Constructor
*
* @param $table string the name of the database table
*
* @param $keycolumn mixed string with name of key column, or array of
* strings if the table has a primary key of more than one column
*
* @param $dbh object database connection object
*
* @param $validator mixed function or method used to validate
* each new value, called with three parameters: the name of the
* field/column that is changing, a reference to the new value and
* a reference to this object
*
*/
public function __construct($table, $keycolumn, &$dbh, $validator = null)
{
$this->PEAR('DB_Error');
$this->_table = $table;
$this->_keycolumn = $keycolumn;
$this->_dbh = $dbh;
$this->_readonly = false;
$this->_validator = $validator;
}
// }}}
// {{{ _makeWhere()
/**
* Create a new (empty) row in the configured table for this
* object.
* @param $newpk
* @return |null
*/
public function insert($newpk)
{
if (is_array($this->_keycolumn)) {
$primarykey = $this->_keycolumn;
} else {
$primarykey = array($this->_keycolumn);
}
settype($newpk, "array");
for ($i = 0; $i < sizeof($primarykey); $i++) {
$pkvals[] = $this->_dbh->quote($newpk[$i]);
}
$sth = $this->_dbh->query("INSERT INTO $this->_table (" .
implode(",", $primarykey) . ") VALUES(" .
implode(",", $pkvals) . ")");
if (DB::isError($sth)) {
return $sth;
}
if (sizeof($newpk) == 1) {
$newpk = $newpk[0];
}
$this->setup($newpk);
return null;
}
// }}}
// {{{ setup()
/**
* Method used to initialize a DB_storage object from the
* configured table.
*
* @param $keyval mixed the key[s] of the row to fetch (string or array)
*
* @return int|object
*/
public function setup($keyval)
{
$whereclause = $this->_makeWhere($keyval);
$query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause;
$sth = $this->_dbh->query($query);
if (DB::isError($sth)) {
return $sth;
}
$row = $sth->fetchRow(DB_FETCHMODE_ASSOC);
if (DB::isError($row)) {
return $row;
}
if (!$row) {
return $this->raiseError(
null,
DB_ERROR_NOT_FOUND,
null,
null,
$query,
null,
true
);
}
foreach ($row as $key => $value) {
$this->_properties[$key] = true;
$this->$key = $value;
}
return DB_OK;
}
// }}}
// {{{ insert()
/**
* Utility method to build a "WHERE" clause to locate ourselves in
* the table.
*
* XXX future improvement: use rowids?
*
* @access private
* @param null $keyval
* @return mixed|string|null
*/
public function _makeWhere($keyval = null)
{
if (is_array($this->_keycolumn)) {
if ($keyval === null) {
for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
$keyval[] = $this->{$this->_keycolumn[$i]};
}
}
$whereclause = '';
for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
if ($i > 0) {
$whereclause .= ' AND ';
}
$whereclause .= $this->_keycolumn[$i];
if (is_null($keyval[$i])) {
// there's not much point in having a NULL key,
// but we support it anyway
$whereclause .= ' IS NULL';
} else {
$whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]);
}
}
} else {
if ($keyval === null) {
$keyval = @$this->{$this->_keycolumn};
}
$whereclause = $this->_keycolumn;
if (is_null($keyval)) {
// there's not much point in having a NULL key,
// but we support it anyway
$whereclause .= ' IS NULL';
} else {
$whereclause .= ' = ' . $this->_dbh->quote($keyval);
}
}
return $whereclause;
}
// }}}
// {{{ toString()
/**
* Output a simple description of this DB_storage object.
* @return string object description
*/
public function toString()
{
$info = strtolower(get_class($this));
$info .= " (table=";
$info .= $this->_table;
$info .= ", keycolumn=";
if (is_array($this->_keycolumn)) {
$info .= "(" . implode(",", $this->_keycolumn) . ")";
} else {
$info .= $this->_keycolumn;
}
$info .= ", dbh=";
if (is_object($this->_dbh)) {
$info .= $this->_dbh->toString();
} else {
$info .= "null";
}
$info .= ")";
if (sizeof($this->_properties)) {
$info .= " [loaded, key=";
$keyname = $this->_keycolumn;
if (is_array($keyname)) {
$info .= "(";
for ($i = 0; $i < sizeof($keyname); $i++) {
if ($i > 0) {
$info .= ",";
}
$info .= $this->$keyname[$i];
}
$info .= ")";
} else {
$info .= $this->$keyname;
}
$info .= "]";
}
if (sizeof($this->_changes)) {
$info .= " [modified]";
}
return $info;
}
// }}}
// {{{ dump()
/**
* Dump the contents of this object to "standard output".
*/
public function dump()
{
foreach ($this->_properties as $prop => $foo) {
print "$prop = ";
print htmlentities($this->$prop);
print "<br />\n";
}
}
// }}}
// {{{ &create()
/**
* Static method used to create new DB storage objects.
* @param $table
* @param $data assoc. array where the keys are the names
* of properties/columns
* @return object a new instance of DB_storage or a subclass of it
*/
public function &create($table, &$data)
{
$classname = strtolower(get_class($this));
$obj = new $classname($table);
foreach ($data as $name => $value) {
$obj->_properties[$name] = true;
$obj->$name = &$value;
}
return $obj;
}
// }}}
// {{{ loadFromQuery()
/**
* Loads data into this object from the given query. If this
* object already contains table data, changes will be saved and
* the object re-initialized first.
*
* @param $query SQL query
*
* @param $params parameter list in case you want to use
* prepare/execute mode
*
* @return int DB_OK on success, DB_WARNING_READ_ONLY if the
* returned object is read-only (because the object's specified
* key column was not found among the columns returned by $query),
* or another DB error code in case of errors.
*/
// XXX commented out for now
/*
function loadFromQuery($query, $params = null)
{
if (sizeof($this->_properties)) {
if (sizeof($this->_changes)) {
$this->store();
$this->_changes = array();
}
$this->_properties = array();
}
$rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params);
if (DB::isError($rowdata)) {
return $rowdata;
}
reset($rowdata);
$found_keycolumn = false;
while (list($key, $value) = each($rowdata)) {
if ($key == $this->_keycolumn) {
$found_keycolumn = true;
}
$this->_properties[$key] = true;
$this->$key = &$value;
unset($value); // have to unset, or all properties will
// refer to the same value
}
if (!$found_keycolumn) {
$this->_readonly = true;
return DB_WARNING_READ_ONLY;
}
return DB_OK;
}
*/
// }}}
// {{{ set()
/**
* Modify an attriute value.
* @param $property
* @param $newvalue
* @return bool|object
*/
public function set($property, $newvalue)
{
// only change if $property is known and object is not
// read-only
if ($this->_readonly) {
return $this->raiseError(
null,
DB_WARNING_READ_ONLY,
null,
null,
null,
null,
true
);
}
if (@isset($this->_properties[$property])) {
if (empty($this->_validator)) {
$valid = true;
} else {
$valid = @call_user_func(
$this->_validator,
$this->_table,
$property,
$newvalue,
$this->$property,
$this
);
}
if ($valid) {
$this->$property = $newvalue;
if (empty($this->_changes[$property])) {
$this->_changes[$property] = 0;
} else {
$this->_changes[$property]++;
}
} else {
return $this->raiseError(
null,
DB_ERROR_INVALID,
null,
null,
"invalid field: $property",
null,
true
);
}
return true;
}
return $this->raiseError(
null,
DB_ERROR_NOSUCHFIELD,
null,
null,
"unknown field: $property",
null,
true
);
}
// }}}
// {{{ &get()
/**
* Fetch an attribute value.
*
* @param string attribute name
*
* @return attribute contents, or null if the attribute name is
* unknown
*/
public function &get($property)
{
// only return if $property is known
if (isset($this->_properties[$property])) {
return $this->$property;
}
$tmp = null;
return $tmp;
}
// }}}
// {{{ _DB_storage()
/**
* Destructor, calls DB_storage::store() if there are changes
* that are to be kept.
*/
public function _DB_storage()
{
if (sizeof($this->_changes)) {
$this->store();
}
$this->_properties = array();
$this->_changes = array();
$this->_table = null;
}
// }}}
// {{{ store()
/**
* Stores changes to this object in the database.
*
* @return DB_OK|int
*/
public function store()
{
$params = array();
$vars = array();
foreach ($this->_changes as $name => $foo) {
$params[] = &$this->$name;
$vars[] = $name . ' = ?';
}
if ($vars) {
$query = 'UPDATE ' . $this->_table . ' SET ' .
implode(', ', $vars) . ' WHERE ' .
$this->_makeWhere();
$stmt = $this->_dbh->prepare($query);
$res = $this->_dbh->execute($stmt, $params);
if (DB::isError($res)) {
return $res;
}
$this->_changes = array();
}
return DB_OK;
}
// }}}
// {{{ remove()
/**
* Remove the row represented by this object from the database.
*
* @return mixed DB_OK or a DB error
*/
public function remove()
{
if ($this->_readonly) {
return $this->raiseError(
null,
DB_WARNING_READ_ONLY,
null,
null,
null,
null,
true
);
}
$query = 'DELETE FROM ' . $this->_table . ' WHERE ' .
$this->_makeWhere();
$res = $this->_dbh->query($query);
if (DB::isError($res)) {
return $res;
}
foreach ($this->_properties as $prop => $foo) {
unset($this->$prop);
}
$this->_properties = array();
$this->_changes = array();
return DB_OK;
}
// }}}
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/

View File

@ -1,955 +0,0 @@
<?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 version 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<EFBFBD>nio Carlos Ven<EFBFBD>ncio J<EFBFBD>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$
* @link http://pear.php.net/package/DB
*/
/**
* Obtain the DB_common class so it can be extended from
*/
//require_once 'DB/common.php';
require_once '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<EFBFBD>nio Carlos Ven<EFBFBD>ncio J<EFBFBD>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.9.2
* @link http://pear.php.net/package/DB
*/
class DB_sybase extends DB_common
{
// {{{ properties
/**
* The DB driver type (mysql, oci8, odbc, etc.)
* @var string
*/
public $phptype = 'sybase';
/**
* The database syntax variant to be used (db2, access, etc.), if any
* @var string
*/
public $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
*/
public $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
*/
public $errorcode_map = array();
/**
* The raw database connection created by PHP
* @var resource
*/
public $connection;
/**
* The DSN information for connecting to a database
* @var array
*/
public $dsn = array();
/**
* Should data manipulation queries be committed automatically?
* @var bool
* @access private
*/
public $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
*/
public $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
*/
public $_db = '';
// }}}
// {{{ constructor
/**
* This constructor calls <kbd>parent::__construct()</kbd>
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
// }}}
// {{{ 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|object
*/
public 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
*/
public 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
*/
public 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()
/**
* 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()
*/
public function sybaseRaiseError($errno = null)
{
$native = $this->errorNative();
if ($errno === null) {
$errno = $this->errorCode($native);
}
return $this->raiseError($errno, null, null, null, $native);
}
// }}}
// {{{ fetchInto()
/**
* Gets the DBMS' native error message produced by the last query
*
* @return string the DBMS' error message
*/
public function errorNative()
{
return @sybase_get_last_message();
}
// }}}
// {{{ freeResult()
/**
* 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
*/
public 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;
}
// }}}
// {{{ numCols()
/**
* 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
*/
public function nextResult($result)
{
return false;
}
// }}}
// {{{ numRows()
/**
* 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()
*/
public 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;
}
// }}}
// {{{ affectedRows()
/**
* 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()
*/
public function freeResult($result)
{
return is_resource($result) ? sybase_free_result($result) : false;
}
// }}}
// {{{ nextId()
/**
* 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|object
*
* @see DB_result::numCols()
*/
public function numCols($result)
{
$cols = @sybase_num_fields($result);
if (!$cols) {
return $this->sybaseRaiseError();
}
return $cols;
}
/**
* 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|object
*
* @see DB_result::numRows()
*/
public function numRows($result)
{
$rows = @sybase_num_rows($result);
if ($rows === false) {
return $this->sybaseRaiseError();
}
return $rows;
}
// }}}
// {{{ dropSequence()
/**
* 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.
*/
public function affectedRows()
{
if ($this->_last_query_manip) {
$result = @sybase_affected_rows($this->connection);
} else {
$result = 0;
}
return $result;
}
// }}}
// {{{ quoteFloat()
/**
* 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|object
* A DB_Error object on failure.
*
* @see DB_common::nextID(), DB_common::getSequenceName(),
* DB_sybase::createSequence(), DB_sybase::dropSequence()
*/
public 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];
}
// }}}
// {{{ autoCommit()
/**
* 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()
*/
public function createSequence($seq_name)
{
return $this->query('CREATE TABLE '
. $this->getSequenceName($seq_name)
. ' (id numeric(10, 0) IDENTITY NOT NULL,'
. ' vapor int NULL)');
}
// }}}
// {{{ commit()
/**
* 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()
*/
public function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
// }}}
// {{{ rollback()
/**
* 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.
*/
public function quoteFloat($float)
{
return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
}
// }}}
// {{{ sybaseRaiseError()
/**
* 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.
*/
public 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;
}
// }}}
// {{{ errorNative()
/**
* Commits the current transaction
*
* @return int|object
*/
public 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;
}
// }}}
// {{{ errorCode()
/**
* Reverts the current transaction
*
* @return int|object
*/
public 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;
}
// }}}
// {{{ 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|object
* A DB_Error object on failure.
*
* @see DB_common::tableInfo()
* @since Method available since Release 1.6.0
*/
public 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
*/
public 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
*/
public 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()
*/
public 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:
*/

4571
extlib/MDB2.php Normal file

File diff suppressed because it is too large Load Diff

183
extlib/MDB2/Date.php Normal file
View File

@ -0,0 +1,183 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
/**
* Several methods to convert the MDB2 native timestamp format (ISO based)
* to and from data structures that are convenient to worth with in side of php.
* For more complex date arithmetic please take a look at the Date package in PEAR
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Date
{
// {{{ mdbNow()
/**
* return the current datetime
*
* @return string current datetime in the MDB2 format
* @access public
*/
public static function mdbNow()
{
return date('Y-m-d H:i:s');
}
// }}}
// {{{ mdbToday()
/**
* return the current date
*
* @return string current date in the MDB2 format
* @access public
*/
public static function mdbToday()
{
return date('Y-m-d');
}
// }}}
// {{{ mdbTime()
/**
* return the current time
*
* @return string current time in the MDB2 format
* @access public
*/
public static function mdbTime()
{
return date('H:i:s');
}
// }}}
// {{{ date2Mdbstamp()
/**
* convert a date into a MDB2 timestamp
*
* @param int hour of the date
* @param int minute of the date
* @param int second of the date
* @param int month of the date
* @param int day of the date
* @param int year of the date
*
* @return string a valid MDB2 timestamp
* @access public
*/
public static function date2Mdbstamp($hour = null, $minute = null, $second = null,
$month = null, $day = null, $year = null)
{
return MDB2_Date::unix2Mdbstamp(mktime($hour, $minute, $second, $month, $day, $year, -1));
}
// }}}
// {{{ unix2Mdbstamp()
/**
* convert a unix timestamp into a MDB2 timestamp
*
* @param int a valid unix timestamp
*
* @return string a valid MDB2 timestamp
* @access public
*/
public static function unix2Mdbstamp($unix_timestamp)
{
return date('Y-m-d H:i:s', $unix_timestamp);
}
// }}}
// {{{ mdbstamp2Unix()
/**
* convert a MDB2 timestamp into a unix timestamp
*
* @param int a valid MDB2 timestamp
* @return string unix timestamp with the time stored in the MDB2 format
*
* @access public
*/
public static function mdbstamp2Unix($mdb_timestamp)
{
$arr = MDB2_Date::mdbstamp2Date($mdb_timestamp);
return mktime($arr['hour'], $arr['minute'], $arr['second'], $arr['month'], $arr['day'], $arr['year'], -1);
}
// }}}
// {{{ mdbstamp2Date()
/**
* convert a MDB2 timestamp into an array containing all
* values necessary to pass to php's date() function
*
* @param int a valid MDB2 timestamp
*
* @return array with the time split
* @access public
*/
public static function mdbstamp2Date($mdb_timestamp)
{
list($arr['year'], $arr['month'], $arr['day'], $arr['hour'], $arr['minute'], $arr['second']) =
sscanf($mdb_timestamp, "%04u-%02u-%02u %02u:%02u:%02u");
return $arr;
}
// }}}
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,350 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 FrontbaseSQL driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_fbsql extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* general type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param string $rtrim if text should be rtrimmed
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (null === $value) {
return null;
}
switch ($type) {
case 'boolean':
return $value == 'T';
case 'time':
if ($value[0] == '+') {
return substr($value, 1);
} else {
return $value;
}
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : $db->options['default_text_field_length'];
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? 'CHAR('.$length.')' : 'VARCHAR('.$length.')';
case 'clob':
return 'CLOB';
case 'blob':
return 'BLOB';
case 'integer':
return 'INT';
case 'boolean':
return 'BOOLEAN';
case 'date':
return 'DATE';
case 'time':
return 'TIME';
case 'timestamp':
return 'TIMESTAMP';
case 'float':
return 'FLOAT';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _quoteBoolean()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBoolean($value, $quote, $escape_wildcards)
{
return ($value ? 'True' : 'False');
}
// }}}
// {{{ _quoteDate()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteDate($value, $quote, $escape_wildcards)
{
return 'DATE'.$this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteTimestamp()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteTimestamp($value, $quote, $escape_wildcards)
{
return 'TIMESTAMP'.$this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteTime()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteTime($value, $quote, $escape_wildcards)
{
return 'TIME'.$this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$length = $field['length'];
if ($length == '-1' && !empty($field['atttypmod'])) {
$length = $field['atttypmod'] - 4;
}
$type = array();
$unsigned = $fixed = null;
switch ($db_type) {
case 'tinyint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 1;
break;
case 'smallint':
$type[] = 'integer';
$unsigned = false;
$length = 2;
if ($length == '2') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
break;
case 'int':
case 'integer':
$type[] = 'integer';
$unsigned = false;
$length = 4;
break;
case 'longint':
$type[] = 'integer';
$unsigned = false;
$length = 8;
break;
case 'boolean':
$type[] = 'boolean';
$length = null;
break;
case 'character varying':
case 'char varying':
case 'varchar':
case 'national character varying':
case 'national char varying':
case 'nchar varying':
$fixed = false;
case 'unknown':
case 'char':
case 'character':
case 'national character':
case 'national char':
case 'nchar':
$type[] = 'text';
if (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'timestamp':
case 'timestamp with time zone':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
case 'time with time zone':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'double precision':
case 'real':
$type[] = 'float';
break;
case 'decimal':
case 'numeric':
$type[] = 'decimal';
break;
case 'blob':
$type[] = 'blob';
$length = null;
break;
case 'clob':
$type[] = 'clob';
$length = null;
break;
default:
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>

View File

@ -0,0 +1,455 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 Firebird/Interbase driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Datatype_ibase extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* General type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (null === $value) {
return null;
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if (MDB2::isError($connection)) {
return $connection;
}
switch ($type) {
case 'text':
$blob_info = @ibase_blob_info($connection, $value);
if (is_array($blob_info) && $blob_info['length'] > 0) {
//LOB => fetch into variable
$clob = $this->_baseConvertResult($value, 'clob', $rtrim);
if (!MDB2::isError($clob) && is_resource($clob)) {
$clob_value = '';
while (!feof($clob)) {
$clob_value .= fread($clob, 8192);
}
$this->destroyLOB($clob);
}
$value = $clob_value;
}
if ($rtrim) {
$value = rtrim($value);
}
return $value;
case 'timestamp':
return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS'));
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ _getCharsetFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $charset name of the charset
* @return string DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration.
*/
function _getCharsetFieldDeclaration($charset)
{
return 'CHARACTER SET '.$charset;
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : $db->options['default_text_field_length'];
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? 'CHAR('.$length.')' : 'VARCHAR('.$length.')';
case 'clob':
return 'BLOB SUB_TYPE 1';
case 'blob':
return 'BLOB SUB_TYPE 0';
case 'integer':
return 'INT';
case 'boolean':
return 'SMALLINT';
case 'date':
return 'DATE';
case 'time':
return 'TIME';
case 'timestamp':
return 'TIMESTAMP';
case 'float':
return 'DOUBLE PRECISION';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _quoteLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteLOB($value, $quote, $escape_wildcards)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$connection = $db->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
$close = true;
if (is_resource($value)) {
$close = false;
} elseif (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
if ($match[1] == 'file://') {
$value = $match[2];
}
$value = @fopen($value, 'r');
} else {
$fp = @tmpfile();
@fwrite($fp, $value);
@rewind($fp);
$value = $fp;
}
$blob_id = @ibase_blob_import($connection, $value);
if ($close) {
@fclose($value);
}
return $blob_id;
}
// }}}
// {{{ _retrieveLOB()
/**
* retrieve LOB from the database
*
* @param array $lob array
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access protected
*/
function _retrieveLOB(&$lob)
{
if (empty($lob['handle'])) {
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$connection = $db->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
$lob['handle'] = @ibase_blob_open($connection, $lob['resource']);
if (!$lob['handle']) {
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(null, null, null,
'Could not open fetched large object field', __FUNCTION__);
}
}
$lob['loaded'] = true;
return MDB2_OK;
}
// }}}
// {{{ _readLOB()
/**
* Read data from large object input stream.
*
* @param array $lob array
* @param blob $data reference to a variable that will hold data to be
* read from the large object input stream
* @param int $length integer value that indicates the largest ammount of
* data to be read from the large object input stream.
* @return mixed length on success, a MDB2 error on failure
* @access protected
*/
function _readLOB(&$lob, $length)
{
$data = @ibase_blob_get($lob['handle'], $length);
if (!is_string($data)) {
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(null, null, null,
'Unable to read LOB', __FUNCTION__);
}
return $data;
}
// }}}
// {{{ _destroyLOB()
/**
* Free any resources allocated during the lifetime of the large object
* handler object.
*
* @param array $lob array
* @access protected
*/
function _destroyLOB(&$lob)
{
if (isset($lob['handle'])) {
@ibase_blob_close($lob['handle']);
unset($lob['handle']);
}
}
// }}}
// {{{ patternEscapeString()
/**
* build string to define escape pattern string
*
* @access public
*
* @return string define escape pattern
*/
function patternEscapeString()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return " ESCAPE '". $db->string_quoting['escape_pattern'] ."'";
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$length = $field['length'];
if ((int)$length <= 0) {
$length = null;
}
$type = array();
$unsigned = $fixed = null;
$db_type = strtolower($field['type']);
$field['field_sub_type'] = !empty($field['field_sub_type'])
? strtolower($field['field_sub_type']) : null;
switch ($db_type) {
case 'smallint':
case 'integer':
case 'int64':
//these may be 'numeric' or 'decimal'
if (isset($field['field_sub_type'])) {
$field['type'] = $field['field_sub_type'];
return $this->mapNativeDatatype($field);
}
case 'bigint':
case 'quad':
$type[] = 'integer';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
break;
case 'varchar':
$fixed = false;
case 'char':
case 'cstring':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'timestamp':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'double':
case 'double precision':
case 'd_float':
$type[] = 'float';
break;
case 'decimal':
case 'numeric':
$type[] = 'decimal';
$length = $field['precision'].','.$field['scale'];
break;
case 'blob':
$type[] = ($field['field_sub_type'] == 'text') ? 'clob' : 'blob';
$length = null;
break;
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>

View File

@ -0,0 +1,497 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 MS SQL driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_mssql extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* general type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (null === $value) {
return null;
}
switch ($type) {
case 'boolean':
return ($value === 0)? false : !empty($value);
case 'date':
if (strlen($value) > 10) {
$value = substr($value,0,10);
}
return $value;
case 'time':
if (strlen($value) > 8) {
$value = substr($value,11,8);
}
return $value;
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 8000) {
return 'VARCHAR('.$length.')';
}
}
return 'TEXT';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 8000) {
return "VARBINARY($length)";
}
}
return 'IMAGE';
case 'integer':
return 'INT';
case 'boolean':
return 'BIT';
case 'date':
return 'CHAR ('.strlen('YYYY-MM-DD').')';
case 'time':
return 'CHAR ('.strlen('HH:MM:SS').')';
case 'timestamp':
return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
case 'float':
return 'FLOAT';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' IDENTITY PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = 0;
}
if (null === $field['default']) {
$default = ' DEFAULT (null)';
} else {
$default = ' DEFAULT (' . $this->quote($field['default'], 'integer') . ')';
}
}
if (!empty($field['unsigned'])) {
$db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
}
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull.$default.$autoinc;
}
// }}}
// {{{ _getCLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an character
* large object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function _getCLOBDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _getBLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an binary large
* object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getBLOBDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _quoteBLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBLOB($value, $quote, $escape_wildcards)
{
if (!$quote) {
return $value;
}
$value = '0x'.bin2hex($this->_readFile($value));
return $value;
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$match = '';
if (null !== $operator) {
$field = (null === $field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'LIKE ';
break;
case 'NOT ILIKE':
$match = $field.'NOT LIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE ';
break;
case 'NOT LIKE':
$match = $field.'NOT LIKE ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
// todo: handle length of various int variations
$db_type = preg_replace('/\d/', '', strtolower($field['type']));
$length = $field['length'];
$type = array();
// todo: unsigned handling seems to be missing
$unsigned = $fixed = null;
switch ($db_type) {
case 'bit':
$type[0] = 'boolean';
break;
case 'tinyint':
$type[0] = 'integer';
$length = 1;
break;
case 'smallint':
$type[0] = 'integer';
$length = 2;
break;
case 'int':
$type[0] = 'integer';
$length = 4;
break;
case 'bigint':
$type[0] = 'integer';
$length = 8;
break;
case 'smalldatetime':
case 'datetime':
$type[0] = 'timestamp';
break;
case 'float':
case 'real':
case 'numeric':
$type[0] = 'float';
break;
case 'decimal':
case 'money':
$type[0] = 'decimal';
$length = $field['numeric_precision'].','.$field['numeric_scale'];
break;
case 'text':
case 'ntext':
case 'varchar':
case 'nvarchar':
$fixed = false;
case 'char':
case 'nchar':
$type[0] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'image':
case 'varbinary':
case 'timestamp':
$type[] = 'blob';
$length = null;
break;
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>

View File

@ -0,0 +1,640 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 MySQLi driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_mysqli extends MDB2_Driver_Datatype_Common
{
// {{{ _getCharsetFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $charset name of the charset
* @return string DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration.
*/
function _getCharsetFieldDeclaration($charset)
{
return 'CHARACTER SET '.$charset;
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare
* of the given type
*
* @param string $type type to which the value should be converted to
* @param string $name name the field to be declared.
* @param string $field definition of the field
*
* @return string DBMS-specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getDeclaration($type, $name, $field)
{
// MySQL DDL syntax forbids combining NOT NULL with DEFAULT NULL.
// To get a default of NULL for NOT NULL columns, omit it.
if ( isset($field['notnull'])
&& !empty($field['notnull'])
&& array_key_exists('default', $field) // do not use isset() here!
&& null === $field['default']
) {
unset($field['default']);
}
return parent::getDeclaration($type, $name, $field);
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
if (empty($field['length']) && array_key_exists('default', $field)) {
$field['length'] = $db->varchar_max_length;
}
$length = !empty($field['length']) ? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR(255)')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYTEXT';
} elseif ($length <= 65532) {
return 'TEXT';
} elseif ($length <= 16777215) {
return 'MEDIUMTEXT';
}
}
return 'LONGTEXT';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYBLOB';
} elseif ($length <= 65532) {
return 'BLOB';
} elseif ($length <= 16777215) {
return 'MEDIUMBLOB';
}
}
return 'LONGBLOB';
case 'integer':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 1) {
return 'TINYINT';
} elseif ($length == 2) {
return 'SMALLINT';
} elseif ($length == 3) {
return 'MEDIUMINT';
} elseif ($length == 4) {
return 'INT';
} elseif ($length > 4) {
return 'BIGINT';
}
}
return 'INT';
case 'boolean':
return 'TINYINT(1)';
case 'date':
return 'DATE';
case 'time':
return 'TIME';
case 'timestamp':
return 'DATETIME';
case 'float':
$l = '';
if (!empty($field['length'])) {
$l = '(' . $field['length'];
if (!empty($field['scale'])) {
$l .= ',' . $field['scale'];
}
$l .= ')';
}
return 'DOUBLE' . $l;
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' AUTO_INCREMENT PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
if (empty($default) && empty($notnull)) {
$default = ' DEFAULT NULL';
}
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
}
// }}}
// {{{ _getFloatDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an float type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned float if
* possible.
*
* default
* float value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getFloatDeclaration($name, $field)
{
// Since AUTO_INCREMENT can be used for integer or floating-point types,
// reuse the INTEGER declaration
// @see http://bugs.mysql.com/bug.php?id=31032
return $this->_getIntegerDeclaration($name, $field);
}
// }}}
// {{{ _getDecimalDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an decimal type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Decimal value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getDecimalDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$default = '';
if (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
} elseif (empty($field['notnull'])) {
$default = ' DEFAULT NULL';
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull;
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$match = '';
if (null !== $operator) {
$field = (null === $field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'LIKE ';
break;
case 'NOT ILIKE':
$match = $field.'NOT LIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE BINARY ';
break;
case 'NOT LIKE':
$match = $field.'NOT LIKE BINARY ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$db_type = strtok($db_type, '(), ');
if ($db_type == 'national') {
$db_type = strtok('(), ');
}
if (!empty($field['length'])) {
$length = strtok($field['length'], ', ');
$decimal = strtok(', ');
} else {
$length = strtok('(), ');
$decimal = strtok('(), ');
}
$type = array();
$unsigned = $fixed = null;
switch ($db_type) {
case 'tinyint':
$type[] = 'integer';
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 1;
break;
case 'smallint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 2;
break;
case 'mediumint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 3;
break;
case 'int':
case 'integer':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 4;
break;
case 'bigint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 8;
break;
case 'tinytext':
case 'mediumtext':
case 'longtext':
case 'text':
case 'varchar':
$fixed = false;
case 'string':
case 'char':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
if ($decimal == 'binary') {
$type[] = 'blob';
}
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'enum':
$type[] = 'text';
preg_match_all('/\'.+\'/U', $field['type'], $matches);
$length = 0;
$fixed = false;
if (is_array($matches)) {
foreach ($matches[0] as $value) {
$length = max($length, strlen($value)-2);
}
if ($length == '1' && count($matches[0]) == 2) {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
}
$type[] = 'integer';
case 'set':
$fixed = false;
$type[] = 'text';
$type[] = 'integer';
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'datetime':
case 'timestamp':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'double':
case 'real':
$type[] = 'float';
$unsigned = preg_match('/ unsigned/i', $field['type']);
if ($decimal !== false) {
$length = $length.','.$decimal;
}
break;
case 'unknown':
case 'decimal':
case 'numeric':
$type[] = 'decimal';
$unsigned = preg_match('/ unsigned/i', $field['type']);
if ($decimal !== false) {
$length = $length.','.$decimal;
}
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
$type[] = 'blob';
$length = null;
break;
case 'binary':
case 'varbinary':
$type[] = 'blob';
break;
case 'year':
$type[] = 'integer';
$type[] = 'date';
$length = null;
break;
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
// {{{ mapPrepareDatatype()
/**
* Maps an MDB2 datatype to native prepare type
*
* @param string $type
* @return string
* @access public
*/
function mapPrepareDatatype($type)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map'][$type])) {
$type = $db->options['datatype_map'][$type];
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type);
return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
}
}
switch ($type) {
case 'boolean':
case 'integer':
return 'i';
case 'float':
return 'd';
case 'blob':
return 'b';
default:
break;
}
return 's';
}
// }}}
}
?>

View File

@ -0,0 +1,499 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
// $Id$
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 OCI8 driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_oci8 extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* general type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (null === $value) {
return null;
}
switch ($type) {
case 'text':
if (is_object($value) && is_a($value, 'OCI-Lob')) {
//LOB => fetch into variable
$clob = $this->_baseConvertResult($value, 'clob', $rtrim);
if (!MDB2::isError($clob) && is_resource($clob)) {
$clob_value = '';
while (!feof($clob)) {
$clob_value .= fread($clob, 8192);
}
$this->destroyLOB($clob);
}
$value = $clob_value;
}
if ($rtrim) {
$value = rtrim($value);
}
return $value;
case 'date':
return substr($value, 0, strlen('YYYY-MM-DD'));
case 'time':
return substr($value, strlen('YYYY-MM-DD '), strlen('HH:MI:SS'));
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : $db->options['default_text_field_length'];
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? 'CHAR('.$length.')' : 'VARCHAR2('.$length.')';
case 'clob':
return 'CLOB';
case 'blob':
return 'BLOB';
case 'integer':
if (!empty($field['length'])) {
switch((int)$field['length']) {
case 1: $digit = 3; break;
case 2: $digit = 5; break;
case 3: $digit = 8; break;
case 4: $digit = 10; break;
case 5: $digit = 13; break;
case 6: $digit = 15; break;
case 7: $digit = 17; break;
case 8: $digit = 20; break;
default: $digit = 10;
}
return 'NUMBER('.$digit.')';
}
return 'INT';
case 'boolean':
return 'NUMBER(1)';
case 'date':
case 'time':
case 'timestamp':
return 'DATE';
case 'float':
return 'NUMBER';
case 'decimal':
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'NUMBER(*,'.$scale.')';
}
}
// }}}
// {{{ _quoteCLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteCLOB($value, $quote, $escape_wildcards)
{
return 'EMPTY_CLOB()';
}
// }}}
// {{{ _quoteBLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBLOB($value, $quote, $escape_wildcards)
{
return 'EMPTY_BLOB()';
}
// }}}
// {{{ _quoteDate()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteDate($value, $quote, $escape_wildcards)
{
return $this->_quoteText("$value 00:00:00", $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteTimestamp()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteTimestamp($value, $quote, $escape_wildcards)
{
return $this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteTime()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteTime($value, $quote, $escape_wildcards)
{
return $this->_quoteText("0001-01-01 $value", $quote, $escape_wildcards);
}
// }}}
// {{{ writeLOBToFile()
/**
* retrieve LOB from the database
*
* @param array $lob array
* @param string $file name of the file into which the LOb should be fetched
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access protected
*/
function writeLOBToFile($lob, $file)
{
if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
if ($match[1] == 'file://') {
$file = $match[2];
}
}
$lob_data = stream_get_meta_data($lob);
$lob_index = $lob_data['wrapper_data']->lob_index;
$result = $this->lobs[$lob_index]['resource']->writetofile($file);
$this->lobs[$lob_index]['resource']->seek(0);
if (!$result) {
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(null, null, null,
'Unable to write LOB to file', __FUNCTION__);
}
return MDB2_OK;
}
// }}}
// {{{ _retrieveLOB()
/**
* retrieve LOB from the database
*
* @param array $lob array
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access protected
*/
function _retrieveLOB(&$lob)
{
if (!is_object($lob['resource'])) {
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'attemped to retrieve LOB from non existing or NULL column', __FUNCTION__);
}
if (!$lob['loaded']
# && !method_exists($lob['resource'], 'read')
) {
$lob['value'] = $lob['resource']->load();
$lob['resource']->seek(0);
}
$lob['loaded'] = true;
return MDB2_OK;
}
// }}}
// {{{ _readLOB()
/**
* Read data from large object input stream.
*
* @param array $lob array
* @param blob $data reference to a variable that will hold data to be
* read from the large object input stream
* @param int $length integer value that indicates the largest ammount of
* data to be read from the large object input stream.
* @return mixed length on success, a MDB2 error on failure
* @access protected
*/
function _readLOB($lob, $length)
{
if ($lob['loaded']) {
return parent::_readLOB($lob, $length);
}
if (!is_object($lob['resource'])) {
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'attemped to retrieve LOB from non existing or NULL column', __FUNCTION__);
}
$data = $lob['resource']->read($length);
if (!is_string($data)) {
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(null, null, null,
'Unable to read LOB', __FUNCTION__);
}
return $data;
}
// }}}
// {{{ patternEscapeString()
/**
* build string to define escape pattern string
*
* @access public
*
*
* @return string define escape pattern
*/
function patternEscapeString()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return " ESCAPE '". $db->string_quoting['escape_pattern'] ."'";
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$type = array();
$length = $unsigned = $fixed = null;
if (!empty($field['length'])) {
$length = $field['length'];
}
switch ($db_type) {
case 'integer':
case 'pls_integer':
case 'binary_integer':
$type[] = 'integer';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
break;
case 'varchar':
case 'varchar2':
case 'nvarchar2':
$fixed = false;
case 'char':
case 'nchar':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
case 'timestamp':
$type[] = 'timestamp';
$length = null;
break;
case 'float':
$type[] = 'float';
break;
case 'number':
if (!empty($field['scale'])) {
$type[] = 'decimal';
$length = $length.','.$field['scale'];
} else {
$type[] = 'integer';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
}
break;
case 'long':
$type[] = 'text';
case 'clob':
case 'nclob':
$type[] = 'clob';
break;
case 'blob':
case 'raw':
case 'long raw':
case 'bfile':
$type[] = 'blob';
$length = null;
break;
case 'rowid':
case 'urowid':
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
}
?>

View File

@ -0,0 +1,397 @@
<?php
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 MS SQL driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_odbc extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* general type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (is_null($value)) {
return null;
}
switch ($type) {
case 'boolean':
return $value == '1';
case 'date':
if (strlen($value) > 10) {
$value = substr($value,0,10);
}
return $value;
case 'time':
if (strlen($value) > 8) {
$value = substr($value,11,8);
}
return $value;
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 8000) {
return 'VARCHAR('.$length.')';
}
}
return 'TEXT';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 8000) {
return "VARBINARY($length)";
}
}
return 'IMAGE';
case 'integer':
return 'INT';
case 'boolean':
return 'BIT';
case 'date':
return 'CHAR ('.strlen('YYYY-MM-DD').')';
case 'time':
return 'CHAR ('.strlen('HH:MM:SS').')';
case 'timestamp':
return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
case 'float':
return 'FLOAT';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' IDENTITY PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = 0;
}
if (is_null($field['default'])) {
$default = ' DEFAULT (null)';
} else {
$default = ' DEFAULT (' . $this->quote($field['default'], 'integer') . ')';
}
}
if (!empty($field['unsigned'])) {
$db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
}
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull.$default.$autoinc;
}
// }}}
// {{{ _getCLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an character
* large object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function _getCLOBDeclaration($name, $field)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _getBLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an binary large
* object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getBLOBDeclaration($name, $field)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _quoteBLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBLOB($value, $quote, $escape_wildcards)
{
if (!$quote) {
return $value;
}
$value = '0x'.bin2hex($this->_readFile($value));
return $value;
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
// todo: handle length of various int variations
$db_type = preg_replace('/\d/', '', strtolower($field['type']));
$length = $field['length'];
$type = array();
// todo: unsigned handling seems to be missing
$unsigned = $fixed = null;
switch ($db_type) {
case 'bit':
$type[0] = 'boolean';
break;
case 'tinyint':
$type[0] = 'integer';
$length = 1;
break;
case 'smallint':
$type[0] = 'integer';
$length = 2;
break;
case 'integer':
$type[0] = 'integer';
$length = 4;
break;
case 'bigint':
$type[0] = 'integer';
$length = 8;
break;
case 'smalldatetime':
case 'timestamp':
case 'time':
case 'date':
$type[0] = 'timestamp';
break;
case 'float':
case 'real':
case 'numeric':
$type[0] = 'float';
break;
case 'decimal':
case 'money':
$type[0] = 'decimal';
$length = $field['numeric_precision'].','.$field['numeric_scale'];
break;
case 'text':
case 'ntext':
case 'varchar() for bit data':
case 'varchar':
case 'nvarchar':
$fixed = false;
case 'char':
case 'nchar':
$type[0] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'image':
case 'blob':
case 'vargraphic() ccsid ':
case 'varbinary':
$type[] = 'blob';
$length = null;
break;
default:
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>

View File

@ -0,0 +1,579 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 PostGreSQL driver
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Driver_Datatype_pgsql extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* General type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (null === $value) {
return null;
}
switch ($type) {
case 'boolean':
return ($value == 'f')? false : !empty($value);
case 'float':
return doubleval($value);
case 'date':
return $value;
case 'time':
return substr($value, 0, strlen('HH:MM:SS'));
case 'timestamp':
return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS'));
case 'blob':
$value = pg_unescape_bytea($value);
return parent::_baseConvertResult($value, $type, $rtrim);
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length']) ? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
return 'TEXT';
case 'blob':
return 'BYTEA';
case 'integer':
if (!empty($field['autoincrement'])) {
if (!empty($field['length'])) {
$length = $field['length'];
if ($length > 4) {
return 'BIGSERIAL PRIMARY KEY';
}
}
return 'SERIAL PRIMARY KEY';
}
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 2) {
return 'SMALLINT';
} elseif ($length == 3 || $length == 4) {
return 'INT';
} elseif ($length > 4) {
return 'BIGINT';
}
}
return 'INT';
case 'boolean':
return 'BOOLEAN';
case 'date':
return 'DATE';
case 'time':
return 'TIME without time zone';
case 'timestamp':
return 'TIMESTAMP without time zone';
case 'float':
return 'FLOAT8';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'NUMERIC('.$length.','.$scale.')';
}
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field should be
* declared as unsigned integer if possible.
*
* default
* Integer value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if (!empty($field['unsigned'])) {
$db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
}
if (!empty($field['autoincrement'])) {
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field);
}
$default = '';
if (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
if (empty($default) && empty($notnull)) {
$default = ' DEFAULT NULL';
}
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$default.$notnull;
}
// }}}
// {{{ _quoteCLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteCLOB($value, $quote, $escape_wildcards)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if ($db->options['lob_allow_url_include']) {
$value = $this->_readFile($value);
if (MDB2::isError($value)) {
return $value;
}
}
return $this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteBLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBLOB($value, $quote, $escape_wildcards)
{
if (!$quote) {
return $value;
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if ($db->options['lob_allow_url_include']) {
$value = $this->_readFile($value);
if (MDB2::isError($value)) {
return $value;
}
}
if (version_compare(PHP_VERSION, '5.2.0RC6', '>=')) {
$connection = $db->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
$value = @pg_escape_bytea($connection, $value);
} else {
$value = @pg_escape_bytea($value);
}
return "'".$value."'";
}
// }}}
// {{{ _quoteBoolean()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBoolean($value, $quote, $escape_wildcards)
{
$value = $value ? 't' : 'f';
if (!$quote) {
return $value;
}
return "'".$value."'";
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$match = '';
if (null !== $operator) {
$field = (null === $field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'ILIKE ';
break;
case 'NOT ILIKE':
$match = $field.'NOT ILIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE ';
break;
case 'NOT LIKE':
$match = $field.'NOT LIKE ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ patternEscapeString()
/**
* build string to define escape pattern string
*
* @access public
*
*
* @return string define escape pattern
*/
function patternEscapeString()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return ' ESCAPE '.$this->quote($db->string_quoting['escape_pattern']);
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$length = $field['length'];
$type = array();
$unsigned = $fixed = null;
switch ($db_type) {
case 'smallint':
case 'int2':
$type[] = 'integer';
$unsigned = false;
$length = 2;
if ($length == '2') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
break;
case 'int':
case 'int4':
case 'integer':
case 'serial':
case 'serial4':
$type[] = 'integer';
$unsigned = false;
$length = 4;
break;
case 'bigint':
case 'int8':
case 'bigserial':
case 'serial8':
$type[] = 'integer';
$unsigned = false;
$length = 8;
break;
case 'bool':
case 'boolean':
$type[] = 'boolean';
$length = null;
break;
case 'text':
case 'varchar':
$fixed = false;
case 'unknown':
case 'char':
case 'bpchar':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'datetime':
case 'timestamp':
case 'timestamptz':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'float4':
case 'float8':
case 'double':
case 'real':
$type[] = 'float';
break;
case 'decimal':
case 'money':
case 'numeric':
$type[] = 'decimal';
if (isset($field['scale'])) {
$length = $length.','.$field['scale'];
}
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
case 'bytea':
$type[] = 'blob';
$length = null;
break;
case 'oid':
$type[] = 'blob';
$type[] = 'clob';
$length = null;
break;
case 'year':
$type[] = 'integer';
$type[] = 'date';
$length = null;
break;
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
// {{{ mapPrepareDatatype()
/**
* Maps an mdb2 datatype to native prepare type
*
* @param string $type
* @return string
* @access public
*/
function mapPrepareDatatype($type)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map'][$type])) {
$type = $db->options['datatype_map'][$type];
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type);
return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
}
}
switch ($type) {
case 'integer':
return 'int';
case 'boolean':
return 'bool';
case 'decimal':
case 'float':
return 'numeric';
case 'clob':
return 'text';
case 'blob':
return 'bytea';
default:
break;
}
return $type;
}
// }}}
}
?>

View File

@ -0,0 +1,99 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 QuerySim driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_querysim extends MDB2_Driver_Datatype_Common
{
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
}
?>

View File

@ -0,0 +1,418 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 SQLite driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_sqlite extends MDB2_Driver_Datatype_Common
{
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYTEXT';
} elseif ($length <= 65532) {
return 'TEXT';
} elseif ($length <= 16777215) {
return 'MEDIUMTEXT';
}
}
return 'LONGTEXT';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYBLOB';
} elseif ($length <= 65532) {
return 'BLOB';
} elseif ($length <= 16777215) {
return 'MEDIUMBLOB';
}
}
return 'LONGBLOB';
case 'integer':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 2) {
return 'SMALLINT';
} elseif ($length == 3 || $length == 4) {
return 'INTEGER';
} elseif ($length > 4) {
return 'BIGINT';
}
}
return 'INTEGER';
case 'boolean':
return 'BOOLEAN';
case 'date':
return 'DATE';
case 'time':
return 'TIME';
case 'timestamp':
return 'DATETIME';
case 'float':
return 'DOUBLE'.($db->options['fixed_float'] ? '('.
($db->options['fixed_float']+2).','.$db->options['fixed_float'].')' : '');
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
if (empty($default) && empty($notnull)) {
$default = ' DEFAULT NULL';
}
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$match = '';
if (null !== $operator) {
$field = (null === $field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'LIKE ';
break;
case 'NOT ILIKE':
$match = $field.'NOT LIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE ';
break;
case 'NOT LIKE':
$match = $field.'NOT LIKE ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$length = !empty($field['length']) ? $field['length'] : null;
$unsigned = !empty($field['unsigned']) ? $field['unsigned'] : null;
$fixed = null;
$type = array();
switch ($db_type) {
case 'boolean':
$type[] = 'boolean';
break;
case 'tinyint':
$type[] = 'integer';
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 1;
break;
case 'smallint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 2;
break;
case 'mediumint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 3;
break;
case 'int':
case 'integer':
case 'serial':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 4;
break;
case 'bigint':
case 'bigserial':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 8;
break;
case 'clob':
$type[] = 'clob';
$fixed = false;
break;
case 'tinytext':
case 'mediumtext':
case 'longtext':
case 'text':
case 'varchar':
case 'varchar2':
$fixed = false;
case 'char':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'datetime':
case 'timestamp':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'double':
case 'real':
$type[] = 'float';
break;
case 'decimal':
case 'numeric':
$type[] = 'decimal';
$length = $length.','.$field['decimal'];
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
$type[] = 'blob';
$length = null;
break;
case 'year':
$type[] = 'integer';
$type[] = 'date';
$length = null;
break;
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>

View File

@ -0,0 +1,451 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 - 7 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 SQLite driver
*
* @package MDB2
* @category Database
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Datatype_sqlite3 extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* General type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (null === $value) {
return null;
}
switch ($type) {
case 'decimal':
switch (gettype($value)) {
case 'integer':
//return (float)($value * 1.0);
return sprintf('%0.2f', $value);
case 'string':
if (!strpos($value, '.')) {
$value .= '.00';
}
break;
}
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYTEXT';
} elseif ($length <= 65532) {
return 'TEXT';
} elseif ($length <= 16777215) {
return 'MEDIUMTEXT';
}
}
return 'LONGTEXT';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYBLOB';
} elseif ($length <= 65532) {
return 'BLOB';
} elseif ($length <= 16777215) {
return 'MEDIUMBLOB';
}
}
return 'LONGBLOB';
case 'integer':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 2) {
return 'SMALLINT';
} elseif ($length == 3 || $length == 4) {
return 'INTEGER';
} elseif ($length > 4) {
return 'BIGINT';
}
}
return 'INTEGER';
case 'boolean':
return 'BOOLEAN';
case 'date':
return 'DATE';
case 'time':
return 'TIME';
case 'timestamp':
return 'DATETIME';
case 'float':
return 'DOUBLE'.($db->options['fixed_float'] ? '('.
($db->options['fixed_float']+2).','.$db->options['fixed_float'].')' : '');
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
if (empty($default) && empty($notnull)) {
$default = ' DEFAULT NULL';
}
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$match = '';
if (null !== $operator) {
$field = (null === $field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'LIKE ';
break;
case 'NOT ILIKE':
$match = $field.'NOT LIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE ';
break;
case 'NOT LIKE':
$match = $field.'NOT LIKE ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$length = !empty($field['length']) ? $field['length'] : null;
$unsigned = !empty($field['unsigned']) ? $field['unsigned'] : null;
$fixed = null;
$type = array();
switch ($db_type) {
case 'boolean':
$type[] = 'boolean';
break;
case 'tinyint':
$type[] = 'integer';
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 1;
break;
case 'smallint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 2;
break;
case 'mediumint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 3;
break;
case 'int':
case 'integer':
case 'serial':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 4;
break;
case 'bigint':
case 'bigserial':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 8;
break;
case 'clob':
$type[] = 'clob';
$fixed = false;
break;
case 'tinytext':
case 'mediumtext':
case 'longtext':
case 'text':
case 'varchar':
case 'varchar2':
$fixed = false;
case 'char':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'datetime':
case 'timestamp':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'double':
case 'real':
$type[] = 'float';
break;
case 'decimal':
case 'numeric':
$type[] = 'decimal';
$length = $length.','.$field['decimal'];
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
$type[] = 'blob';
$length = null;
break;
case 'year':
$type[] = 'integer';
$type[] = 'date';
$length = null;
break;
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>

View File

@ -0,0 +1,459 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
require_once 'MDB2/Driver/Datatype/Common.php';
/**
* MDB2 MS SQL driver
*
* @package MDB2
* @category Database
*/
class MDB2_Driver_Datatype_sqlsrv extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* general type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (null === $value) {
return null;
}
switch ($type) {
case 'boolean':
return ($value === 0)? false : !empty($value);
case 'date':
if (strlen($value) > 10) {
$value = substr($value,0,10);
}
return $value;
case 'time':
if (strlen($value) > 8) {
$value = substr($value,11,8);
}
return $value;
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'VARCHAR(MAX)');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 8000) {
return 'VARCHAR('.$length.')';
}
}
return 'VARCHAR(MAX)';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 8000) {
return "VARBINARY($length)";
}
}
return 'IMAGE';
case 'integer':
return 'INT';
case 'boolean':
return 'BIT';
case 'date':
return 'CHAR ('.strlen('YYYY-MM-DD').')';
case 'time':
return 'CHAR ('.strlen('HH:MM:SS').')';
case 'timestamp':
return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
case 'float':
return 'FLOAT';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' IDENTITY PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = 0;
}
if (null === $field['default']) {
$default = ' DEFAULT (NULL)';
} else {
$default = ' DEFAULT (' . $this->quote($field['default'], 'integer') . ')';
}
}
if (!empty($field['unsigned'])) {
$db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
}
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull.$default.$autoinc;
}
// }}}
// {{{ _getCLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an character
* large object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function _getCLOBDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _getBLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an binary large
* object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getBLOBDeclaration($name, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? ' NULL' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _quoteBLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBLOB($value, $quote, $escape_wildcards)
{
if (!$quote) {
return $value;
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if ($db->options['lob_allow_url_include']) {
$value = '0x'.bin2hex($this->_readFile($value));
}
return "'".$value."'";
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
// todo: handle length of various int variations
$db_type = $field['type'];
$length = $field['length'];
$type = array();
// todo: unsigned handling seems to be missing
$unsigned = $fixed = null;
switch ($db_type) {
case 'bit':
case SQLSRV_SQLTYPE_BIT:
$type[0] = 'boolean';
break;
case 'tinyint':
case SQLSRV_SQLTYPE_TINYINT:
$type[0] = 'integer';
$length = 1;
break;
case 'smallint':
case SQLSRV_SQLTYPE_SMALLINT:
$type[0] = 'integer';
$length = 2;
break;
case 'int':
case SQLSRV_SQLTYPE_INT:
$type[0] = 'integer';
$length = 4;
break;
case 'bigint':
case SQLSRV_SQLTYPE_BIGINT:
$type[0] = 'integer';
$length = 8;
break;
case 'datetime':
case SQLSRV_SQLTYPE_DATETIME:
$type[0] = 'timestamp';
break;
case 'float':
case SQLSRV_SQLTYPE_FLOAT:
case 'real':
case SQLSRV_SQLTYPE_REAL:
$type[0] = 'float';
break;
case 'numeric':
case SQLSRV_SQLTYPE_NUMERIC:
case 'decimal':
case SQLSRV_SQLTYPE_DECIMAL:
case 'money':
case SQLSRV_SQLTYPE_MONEY:
$type[0] = 'decimal';
$length = $field['numeric_precision'].','.$field['numeric_scale'];
break;
case 'text':
case SQLSRV_SQLTYPE_TEXT:
case 'ntext':
case SQLSRV_SQLTYPE_NTEXT:
case 'varchar':
case SQLSRV_SQLTYPE_VARCHAR:
case 'nvarchar':
case SQLSRV_SQLTYPE_NVARCHAR:
$fixed = false;
case 'char':
case SQLSRV_SQLTYPE_CHAR:
case 'nchar':
case SQLSRV_SQLTYPE_NCHAR:
$type[0] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text') || strstr($db_type, SQLSRV_SQLTYPE_TEXT)) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'image':
case SQLSRV_SQLTYPE_IMAGE:
case 'varbinary':
case SQLSRV_SQLTYPE_VARBINARY:
case 'timestamp':
case SQLSRV_SQLTYPE_TIMESTAMP:
$type[] = 'blob';
$length = null;
break;
default:
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>

View File

@ -0,0 +1,293 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
/**
* Base class for the function modules that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Function');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_Common extends MDB2_Module_Common
{
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
*
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
// {{{ functionTable()
/**
* return string for internal table used when calling only a function
*
* @return string for internal table used when calling only a function
* @access public
*/
function functionTable()
{
return '';
}
// }}}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time:
* - CURRENT_TIMESTAMP (date and time, TIMESTAMP type)
* - CURRENT_DATE (date, DATE type)
* - CURRENT_TIME (time, TIME type)
*
* @param string $type 'timestamp' | 'time' | 'date'
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'time':
return 'CURRENT_TIME';
case 'date':
return 'CURRENT_DATE';
case 'timestamp':
default:
return 'CURRENT_TIMESTAMP';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (null !== $length) {
return "SUBSTRING($value FROM $position FOR $length)";
}
return "SUBSTRING($value FROM $position)";
}
// }}}
// {{{ replace()
/**
* return string to call a function to get replace inside an SQL statement.
*
* @return string to call a function to get a replace
* @access public
*/
function replace($str, $from_str, $to_str)
{
return "REPLACE($str, $from_str , $to_str)";
}
// }}}
// {{{ concat()
/**
* Returns string to concatenate two or more string parameters
*
* @param string $value1
* @param string $value2
* @param string $values...
*
* @return string to concatenate two strings
* @access public
*/
function concat($value1, $value2)
{
$args = func_get_args();
return "(".implode(' || ', $args).")";
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return 'RAND()';
}
// }}}
// {{{ lower()
/**
* return string to call a function to lower the case of an expression
*
* @param string $expression
*
* @return return string to lower case of an expression
* @access public
*/
function lower($expression)
{
return "LOWER($expression)";
}
// }}}
// {{{ upper()
/**
* return string to call a function to upper the case of an expression
*
* @param string $expression
*
* @return return string to upper case of an expression
* @access public
*/
function upper($expression)
{
return "UPPER($expression)";
}
// }}}
// {{{ length()
/**
* return string to call a function to get the length of a string expression
*
* @param string $expression
*
* @return return string to get the string expression length
* @access public
*/
function length($expression)
{
return "LENGTH($expression)";
}
// }}}
// {{{ guid()
/**
* Returns global unique identifier
*
* @return string to get global unique identifier
* @access public
*/
function guid()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
}
?>

View File

@ -0,0 +1,61 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Function/Common.php';
/**
* MDB2 FrontBase driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_fbsql extends MDB2_Driver_Function_Common
{
}
?>

View File

@ -0,0 +1,108 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/Driver/Function/Common.php';
/**
* MDB2 FireBird/InterBase driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_ibase extends MDB2_Driver_Function_Common
{
// {{{ functionTable()
/**
* return string for internal table used when calling only a function
*
* @return string for internal table used when calling only a function
* @access public
*/
function functionTable()
{
return ' FROM RDB$DATABASE';
}
// }}}
// {{{ length()
/**
* return string to call a function to get a replacement inside an SQL statement.
*
* @return string to call a function to get a replace
* @access public
*/
function length($expression)
{
return "STRLEN($expression)";
}
// }}}
// {{{ replace()
/**
* return string to call a function to get a replacement inside an SQL statement.
*
* @return string to call a function to get a replace
* @access public
*/
function replace($str, $from_str, $to_str)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
}
?>

View File

@ -0,0 +1,193 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Frank M. Kromann <frank@kromann.info> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Function/Common.php';
// {{{ class MDB2_Driver_Function_mssql
/**
* MDB2 MSSQL driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_mssql extends MDB2_Driver_Function_Common
{
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'EXECUTE '.$name;
$query .= $params ? ' '.implode(', ', $params) : '';
return $db->query($query, $types, $result_class, $result_wrap_class);
}
// }}}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time:
* - CURRENT_TIMESTAMP (date and time, TIMESTAMP type)
* - CURRENT_DATE (date, DATE type)
* - CURRENT_TIME (time, TIME type)
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'time':
case 'date':
case 'timestamp':
default:
return 'GETDATE()';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'DATEDIFF(second, \'19700101\', '. $expression.') + DATEDIFF(second, GETDATE(), GETUTCDATE())';
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (null !== $length) {
return "SUBSTRING($value, $position, $length)";
}
return "SUBSTRING($value, $position, LEN($value) - $position + 1)";
}
// }}}
// {{{ concat()
/**
* Returns string to concatenate two or more string parameters
*
* @param string $value1
* @param string $value2
* @param string $values...
* @return string to concatenate two strings
* @access public
**/
function concat($value1, $value2)
{
$args = func_get_args();
return "(".implode(' + ', $args).")";
}
// }}}
// {{{ length()
/**
* return string to call a function to get the length of a string expression
*
* @param string $expression
* @return return string to get the string expression length
* @access public
*/
function length($expression)
{
return "LEN($expression)";
}
// }}}
// {{{ guid()
/**
* Returns global unique identifier
*
* @return string to get global unique identifier
* @access public
*/
function guid()
{
return 'NEWID()';
}
// }}}
}
// }}}
?>

View File

@ -0,0 +1,144 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Function/Common.php';
/**
* MDB2 MySQLi driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_mysqli extends MDB2_Driver_Function_Common
{
// }}}
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$multi_query = $db->getOption('multi_query');
if (!$multi_query) {
$db->setOption('multi_query', true);
}
$query = 'CALL '.$name;
$query .= $params ? '('.implode(', ', $params).')' : '()';
$result = $db->query($query, $types, $result_class, $result_wrap_class);
if (!$multi_query) {
$db->setOption('multi_query', false);
}
return $result;
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'UNIX_TIMESTAMP('. $expression.')';
}
// }}}
// {{{ concat()
/**
* Returns string to concatenate two or more string parameters
*
* @param string $value1
* @param string $value2
* @param string $values...
* @return string to concatenate two strings
* @access public
**/
function concat($value1, $value2)
{
$args = func_get_args();
return "CONCAT(".implode(', ', $args).")";
}
// }}}
// {{{ guid()
/**
* Returns global unique identifier
*
* @return string to get global unique identifier
* @access public
*/
function guid()
{
return 'UUID()';
}
// }}}
}
?>

View File

@ -0,0 +1,187 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
// $Id$
require_once 'MDB2/Driver/Function/Common.php';
/**
* MDB2 oci8 driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_oci8 extends MDB2_Driver_Function_Common
{
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'EXEC '.$name;
$query .= $params ? '('.implode(', ', $params).')' : '()';
return $db->query($query, $types, $result_class, $result_wrap_class);
}
// }}}
// {{{ functionTable()
/**
* return string for internal table used when calling only a function
*
* @return string for internal table used when calling only a function
* @access public
*/
function functionTable()
{
return ' FROM dual';
}
// }}}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time:
* - CURRENT_TIMESTAMP (date and time, TIMESTAMP type)
* - CURRENT_DATE (date, DATE type)
* - CURRENT_TIME (time, TIME type)
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'date':
case 'time':
case 'timestamp':
default:
return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
$utc_offset = 'CAST(SYS_EXTRACT_UTC(SYSTIMESTAMP) AS DATE) - CAST(SYSTIMESTAMP AS DATE)';
$epoch_date = 'to_date(\'19700101\', \'YYYYMMDD\')';
return '(CAST('.$expression.' AS DATE) - '.$epoch_date.' + '.$utc_offset.') * 86400 seconds';
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (null !== $length) {
return "SUBSTR($value, $position, $length)";
}
return "SUBSTR($value, $position)";
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return 'dbms_random.value';
}
// }}}}
// {{{ guid()
/**
* Returns global unique identifier
*
* @return string to get global unique identifier
* @access public
*/
function guid()
{
return 'SYS_GUID()';
}
// }}}}
}
?>

View File

@ -0,0 +1,132 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/Driver/Function/Common.php';
/**
* MDB2 MySQL driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_pgsql extends MDB2_Driver_Function_Common
{
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'SELECT * FROM '.$name;
$query .= $params ? '('.implode(', ', $params).')' : '()';
return $db->query($query, $types, $result_class, $result_wrap_class);
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'EXTRACT(EPOCH FROM DATE_TRUNC(\'seconds\', CAST ((' . $expression . ') AS TIMESTAMP)))';
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (null !== $length) {
return "SUBSTRING(CAST($value AS VARCHAR) FROM $position FOR $length)";
}
return "SUBSTRING(CAST($value AS VARCHAR) FROM $position)";
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return 'RANDOM()';
}
// }}}
}
?>

View File

@ -0,0 +1,162 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Function/Common.php';
/**
* MDB2 SQLite driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_sqlite extends MDB2_Driver_Function_Common
{
// {{{ constructor
/**
* Constructor
*/
function __construct($db_index)
{
parent::__construct($db_index);
// create all sorts of UDFs
}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time.
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'time':
return 'time(\'now\')';
case 'date':
return 'date(\'now\')';
case 'timestamp':
default:
return 'datetime(\'now\')';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'strftime("%s",'. $expression.', "utc")';
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (null !== $length) {
return "substr($value, $position, $length)";
}
return "substr($value, $position, length($value))";
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return '((RANDOM()+2147483648)/4294967296)';
}
// }}}
// {{{ replace()
/**
* return string to call a function to get a replacement inside an SQL statement.
*
* @return string to call a function to get a replace
* @access public
*/
function replace($str, $from_str, $to_str)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
}
?>

View File

@ -0,0 +1,162 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 - 7 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Function/Common.php';
/**
* MDB2 SQLite3 driver for the function modules
*
* @package MDB2
* @category Database
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Function_sqlite3 extends MDB2_Driver_Function_Common
{
// {{{ constructor
/**
* Constructor
*/
function __construct($db_index)
{
parent::__construct($db_index);
// create all sorts of UDFs
}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time.
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'time':
return 'time(\'now\')';
case 'date':
return 'date(\'now\')';
case 'timestamp':
default:
return 'datetime(\'now\')';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'strftime("%s",'. $expression.', "utc")';
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (null !== $length) {
return "substr($value, $position, $length)";
}
return "substr($value, $position, length($value))";
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return '((RANDOM()+9223372036854775808)/18446744073709551616)';
}
// }}}
// {{{ replace()
/**
* return string to call a function to get a replacement inside an SQL statement.
*
* @return string to call a function to get a replace
* @access public
*/
function replace($str, $from_str, $to_str)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
}
?>

View File

@ -0,0 +1,189 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Frank M. Kromann <frank@kromann.info> |
// +----------------------------------------------------------------------+
require_once 'MDB2/Driver/Function/Common.php';
// {{{ class MDB2_Driver_Function_sqlsrv
/**
* MDB2 MSSQL driver for the function modules
*
* @package MDB2
* @category Database
*/
class MDB2_Driver_Function_sqlsrv extends MDB2_Driver_Function_Common
{
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'EXECUTE '.$name;
$query .= $params ? ' '.implode(', ', $params) : '';
return $db->query($query, $types, $result_class, $result_wrap_class);
}
// }}}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time:
* - CURRENT_TIMESTAMP (date and time, TIMESTAMP type)
* - CURRENT_DATE (date, DATE type)
* - CURRENT_TIME (time, TIME type)
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'time':
case 'date':
case 'timestamp':
default:
return 'GETDATE()';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'DATEDIFF(second, \'19700101\', '. $expression.') + DATEDIFF(second, GETDATE(), GETUTCDATE())';
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (null !== $length) {
return "SUBSTRING($value, $position, $length)";
}
return "SUBSTRING($value, $position, LEN($value) - $position + 1)";
}
// }}}
// {{{ concat()
/**
* Returns string to concatenate two or more string parameters
*
* @param string $value1
* @param string $value2
* @param string $values...
* @return string to concatenate two strings
* @access public
**/
function concat($value1, $value2)
{
$args = func_get_args();
return "(".implode(' + ', $args).")";
}
// }}}
// {{{ length()
/**
* return string to call a function to get the length of a string expression
*
* @param string $expression
* @return return string to get the string expression length
* @access public
*/
function length($expression)
{
return "LEN($expression)";
}
// }}}
// {{{ guid()
/**
* Returns global unique identifier
*
* @return string to get global unique identifier
* @access public
*/
function guid()
{
return 'NEWID()';
}
// }}}
}
// }}}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,597 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Manager/Common.php';
/**
* MDB2 FrontBase driver for the management modules
*
* @package MDB2
* @category Database
* @author Frank M. Kromann <frank@kromann.info>
*/
class MDB2_Driver_Manager_fbsql extends MDB2_Driver_Manager_Common
{
// {{{ createDatabase()
/**
* create a new database
*
* @param string $name name of the database that should be created
* @param array $options array with charset, collation info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createDatabase($name, $options = array())
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = "CREATE DATABASE $name";
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ dropDatabase()
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropDatabase($name)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = "DELETE DATABASE $name";
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ dropTable()
/**
* drop an existing table
*
* @param object $dbs database object that is extended by this class
* @param string $name name of the table that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropTable($name)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$result = $db->exec("DROP TABLE $name CASCADE");
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ alterTable()
/**
* alter an existing table
*
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @access public
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function alterTable($name, $changes, $check)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
foreach ($changes as $change_name => $change){
switch ($change_name) {
case 'add':
case 'remove':
case 'change':
case 'rename':
case 'name':
break;
default:
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'change type "'.$change_name.'" not yet supported', __FUNCTION__);
}
}
if ($check) {
return MDB2_OK;
}
$query = '';
if (!empty($changes['name'])) {
$change_name = $db->quoteIdentifier($changes['name'], true);
$query .= 'RENAME TO ' . $change_name;
}
if (!empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $field_name => $field) {
if ($query) {
$query.= ', ';
}
$query.= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
}
}
if (!empty($changes['remove']) && is_array($changes['remove'])) {
foreach ($changes['remove'] as $field_name => $field) {
if ($query) {
$query.= ', ';
}
$field_name = $db->quoteIdentifier($field_name, true);
$query.= 'DROP ' . $field_name;
}
}
$rename = array();
if (!empty($changes['rename']) && is_array($changes['rename'])) {
foreach ($changes['rename'] as $field_name => $field) {
$rename[$field['name']] = $field_name;
}
}
if (!empty($changes['change']) && is_array($changes['change'])) {
foreach ($changes['change'] as $field_name => $field) {
if ($query) {
$query.= ', ';
}
if (isset($rename[$field_name])) {
$old_field_name = $rename[$field_name];
unset($rename[$field_name]);
} else {
$old_field_name = $field_name;
}
$old_field_name = $db->quoteIdentifier($old_field_name, true);
$query.= "CHANGE $old_field_name " . $db->getDeclaration($field['definition']['type'], $old_field_name, $field['definition']);
}
}
if (!empty($rename) && is_array($rename)) {
foreach ($rename as $renamed_field_name => $renamed_field) {
if ($query) {
$query.= ', ';
}
$old_field_name = $rename[$renamed_field_name];
$field = $changes['rename'][$old_field_name];
$query.= 'CHANGE ' . $db->getDeclaration($field['definition']['type'], $old_field_name, $field['definition']);
}
}
if (!$query) {
return MDB2_OK;
}
$name = $db->quoteIdentifier($name, true);
$result = $db->exec("ALTER TABLE $name $query");
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ listDatabases()
/**
* list all databases
*
* @return mixed array of database names on success, a MDB2 error on failure
* @access public
*/
function listDatabases()
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null,
'not capable', __FUNCTION__);
}
// }}}
// {{{ listUsers()
/**
* list all users
*
* @return mixed array of user names on success, a MDB2 error on failure
* @access public
*/
function listUsers()
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->queryCol('SELECT "user_name" FROM information_schema.users');
}
// }}}
// {{{ listTables()
/**
* list all tables in the current database
*
* @return mixed array of table names on success, a MDB2 error on failure
* @access public
*/
function listTables()
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$table_names = $db->queryCol('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');
if (MDB2::isError($table_names)) {
return $table_names;
}
$result = array();
for ($i = 0, $j = count($table_names); $i < $j; ++$i) {
if (!$this->_fixSequenceName($table_names[$i], true)) {
$result[] = $table_names[$i];
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableFields()
/**
* list all fields in a table in the current database
*
* @param string $table name of table that should be used in method
* @return mixed array of field names on success, a MDB2 error on failure
* @access public
*/
function listTableFields($table)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$result = $db->queryCol("SHOW COLUMNS FROM $table");
if (MDB2::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ dropIndex()
/**
* drop existing index
*
* @param string $table name of table that should be used in method
* @param string $name name of the index to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropIndex($table, $name)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
$result = $db->exec("ALTER TABLE $table DROP INDEX $name");
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ listTableIndexes()
/**
* list all indexes in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of index names on success, a MDB2 error on failure
* @access public
*/
function listTableIndexes($table)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$key_name = 'Key_name';
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$key_name = strtolower($key_name);
} else {
$key_name = strtoupper($key_name);
}
}
$table = $db->quoteIdentifier($table, true);
$query = "SHOW INDEX FROM $table";
$indexes = $db->queryCol($query, 'text', $key_name);
if (MDB2::isError($indexes)) {
return $indexes;
}
$result = array();
foreach ($indexes as $index) {
if ($index != 'PRIMARY' && ($index = $this->_fixIndexName($index))) {
$result[$index] = true;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ createSequence()
/**
* create sequence
*
* @param string $seq_name name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createSequence($seq_name, $start = 1)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
$seqcol_name = $db->quoteIdentifier($db->options['seqcol_name'], true);
$query = "CREATE TABLE $sequence_name ($seqcol_name INTEGER DEFAULT UNIQUE, PRIMARY KEY($seqcol_name))";
$res = $db->exec($query);
$res = $db->exec("SET UNIQUE = 1 FOR $sequence_name");
if (MDB2::isError($res)) {
return $res;
}
if ($start == 1) {
return MDB2_OK;
}
$res = $db->exec("INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')');
if (!MDB2::isError($res)) {
return MDB2_OK;
}
// Handle error
$result = $db->exec("DROP TABLE $sequence_name");
if (MDB2::isError($result)) {
return $db->raiseError($result, null, null,
'could not drop inconsistent sequence table', __FUNCTION__);
}
return $db->raiseError($res, null, null,
'could not create sequence table', __FUNCTION__);
}
// }}}
// {{{ dropSequence()
/**
* drop existing sequence
*
* @param string $seq_name name of the sequence to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropSequence($seq_name)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
$result = $db->exec("DROP TABLE $sequence_name CASCADE");
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ listSequences()
/**
* list all sequences in the current database
*
* @return mixed array of sequence names on success, a MDB2 error on failure
* @access public
*/
function listSequences()
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$table_names = $db->queryCol('SHOW TABLES', 'text');
if (MDB2::isError($table_names)) {
return $table_names;
}
$result = array();
for ($i = 0, $j = count($table_names); $i < $j; ++$i) {
if ($sqn = $this->_fixSequenceName($table_names[$i], true)) {
$result[] = $sqn;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
}
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,978 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/Driver/Manager/Common.php';
/**
* MDB2 MySQL driver for the management modules
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Driver_Manager_pgsql extends MDB2_Driver_Manager_Common
{
// {{{ createDatabase()
/**
* create a new database
*
* @param string $name name of the database that should be created
* @param array $options array with charset info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createDatabase($name, $options = array())
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = 'CREATE DATABASE ' . $name;
if (!empty($options['charset'])) {
$query .= ' WITH ENCODING ' . $db->quote($options['charset'], 'text');
}
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ alterDatabase()
/**
* alter an existing database
*
* @param string $name name of the database that is intended to be changed
* @param array $options array with name, owner info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function alterDatabase($name, $options = array())
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = '';
if (!empty($options['name'])) {
$query .= ' RENAME TO ' . $options['name'];
}
if (!empty($options['owner'])) {
$query .= ' OWNER TO ' . $options['owner'];
}
if (empty($query)) {
return MDB2_OK;
}
$query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true) . $query;
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ dropDatabase()
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropDatabase($name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = "DROP DATABASE $name";
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ _getAdvancedFKOptions()
/**
* Return the FOREIGN KEY query section dealing with non-standard options
* as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
*
* @param array $definition
* @return string
* @access protected
*/
function _getAdvancedFKOptions($definition)
{
$query = '';
if (!empty($definition['match'])) {
$query .= ' MATCH '.$definition['match'];
}
if (!empty($definition['onupdate'])) {
$query .= ' ON UPDATE '.$definition['onupdate'];
}
if (!empty($definition['ondelete'])) {
$query .= ' ON DELETE '.$definition['ondelete'];
}
if (!empty($definition['deferrable'])) {
$query .= ' DEFERRABLE';
} else {
$query .= ' NOT DEFERRABLE';
}
if (!empty($definition['initiallydeferred'])) {
$query .= ' INITIALLY DEFERRED';
} else {
$query .= ' INITIALLY IMMEDIATE';
}
return $query;
}
// }}}
// {{{ truncateTable()
/**
* Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
* it falls back to a DELETE FROM TABLE query)
*
* @param string $name name of the table that should be truncated
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function truncateTable($name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$result = $db->exec("TRUNCATE TABLE $name");
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ vacuum()
/**
* Optimize (vacuum) all the tables in the db (or only the specified table)
* and optionally run ANALYZE.
*
* @param string $table table name (all the tables if empty)
* @param array $options an array with driver-specific options:
* - timeout [int] (in seconds) [mssql-only]
* - analyze [boolean] [pgsql and mysql]
* - full [boolean] [pgsql-only]
* - freeze [boolean] [pgsql-only]
*
* @return mixed MDB2_OK success, a MDB2 error on failure
* @access public
*/
function vacuum($table = null, $options = array())
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'VACUUM';
if (!empty($options['full'])) {
$query .= ' FULL';
}
if (!empty($options['freeze'])) {
$query .= ' FREEZE';
}
if (!empty($options['analyze'])) {
$query .= ' ANALYZE';
}
if (!empty($table)) {
$query .= ' '.$db->quoteIdentifier($table, true);
}
$result = $db->exec($query);
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ alterTable()
/**
* alter an existing table
*
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @access public
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function alterTable($name, $changes, $check)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
foreach ($changes as $change_name => $change) {
switch ($change_name) {
case 'add':
case 'remove':
case 'change':
case 'name':
case 'rename':
break;
default:
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'change type "'.$change_name.'\" not yet supported', __FUNCTION__);
}
}
if ($check) {
return MDB2_OK;
}
$name = $db->quoteIdentifier($name, true);
if (!empty($changes['remove']) && is_array($changes['remove'])) {
foreach ($changes['remove'] as $field_name => $field) {
$field_name = $db->quoteIdentifier($field_name, true);
$query = 'DROP ' . $field_name;
$result = $db->exec("ALTER TABLE $name $query");
if (MDB2::isError($result)) {
return $result;
}
}
}
if (!empty($changes['rename']) && is_array($changes['rename'])) {
foreach ($changes['rename'] as $field_name => $field) {
$field_name = $db->quoteIdentifier($field_name, true);
$result = $db->exec("ALTER TABLE $name RENAME COLUMN $field_name TO ".$db->quoteIdentifier($field['name'], true));
if (MDB2::isError($result)) {
return $result;
}
}
}
if (!empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $field_name => $field) {
$query = 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
$result = $db->exec("ALTER TABLE $name $query");
if (MDB2::isError($result)) {
return $result;
}
}
}
if (!empty($changes['change']) && is_array($changes['change'])) {
foreach ($changes['change'] as $field_name => $field) {
$field_name = $db->quoteIdentifier($field_name, true);
if (!empty($field['definition']['type'])) {
$server_info = $db->getServerVersion();
if (MDB2::isError($server_info)) {
return $server_info;
}
if (is_array($server_info) && $server_info['major'] < 8) {
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'changing column type for "'.$change_name.'\" requires PostgreSQL 8.0 or above', __FUNCTION__);
}
$db->loadModule('Datatype', null, true);
$type = $db->datatype->getTypeDeclaration($field['definition']);
$query = "ALTER $field_name TYPE $type USING CAST($field_name AS $type)";
$result = $db->exec("ALTER TABLE $name $query");
if (MDB2::isError($result)) {
return $result;
}
}
if (array_key_exists('default', $field['definition'])) {
$query = "ALTER $field_name SET DEFAULT ".$db->quote($field['definition']['default'], $field['definition']['type']);
$result = $db->exec("ALTER TABLE $name $query");
if (MDB2::isError($result)) {
return $result;
}
}
if (array_key_exists('notnull', $field['definition'])) {
$query = "ALTER $field_name ".($field['definition']['notnull'] ? 'SET' : 'DROP').' NOT NULL';
$result = $db->exec("ALTER TABLE $name $query");
if (MDB2::isError($result)) {
return $result;
}
}
}
}
if (!empty($changes['name'])) {
$change_name = $db->quoteIdentifier($changes['name'], true);
$result = $db->exec("ALTER TABLE $name RENAME TO ".$change_name);
if (MDB2::isError($result)) {
return $result;
}
}
return MDB2_OK;
}
// }}}
// {{{ listDatabases()
/**
* list all databases
*
* @return mixed array of database names on success, a MDB2 error on failure
* @access public
*/
function listDatabases()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'SELECT datname FROM pg_database';
$result2 = $db->standaloneQuery($query, array('text'), false);
if (!MDB2::isResultCommon($result2)) {
return $result2;
}
$result = $result2->fetchCol();
$result2->free();
if (MDB2::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listUsers()
/**
* list all users
*
* @return mixed array of user names on success, a MDB2 error on failure
* @access public
*/
function listUsers()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'SELECT usename FROM pg_user';
$result2 = $db->standaloneQuery($query, array('text'), false);
if (!MDB2::isResultCommon($result2)) {
return $result2;
}
$result = $result2->fetchCol();
$result2->free();
return $result;
}
// }}}
// {{{ listViews()
/**
* list all views in the current database
*
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listViews()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT viewname
FROM pg_views
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
AND viewname !~ '^pg_'";
$result = $db->queryCol($query);
if (MDB2::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableViews()
/**
* list the views in the database that reference a given table
*
* @param string table for which all referenced views should be found
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listTableViews($table)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'SELECT viewname FROM pg_views NATURAL JOIN pg_tables';
$query.= ' WHERE tablename ='.$db->quote($table, 'text');
$result = $db->queryCol($query);
if (MDB2::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listFunctions()
/**
* list all functions in the current database
*
* @return mixed array of function names on success, a MDB2 error on failure
* @access public
*/
function listFunctions()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "
SELECT
proname
FROM
pg_proc pr,
pg_type tp
WHERE
tp.oid = pr.prorettype
AND pr.proisagg = FALSE
AND tp.typname <> 'trigger'
AND pr.pronamespace IN
(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
$result = $db->queryCol($query);
if (MDB2::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableTriggers()
/**
* list all triggers in the database that reference a given table
*
* @param string table for which all referenced triggers should be found
* @return mixed array of trigger names on success, a MDB2 error on failure
* @access public
*/
function listTableTriggers($table = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'SELECT trg.tgname AS trigger_name
FROM pg_trigger trg,
pg_class tbl
WHERE trg.tgrelid = tbl.oid';
if (null !== $table) {
$table = $db->quote(strtoupper($table), 'text');
$query .= " AND UPPER(tbl.relname) = $table";
}
$result = $db->queryCol($query);
if (MDB2::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTables()
/**
* list all tables in the current database
*
* @return mixed array of table names on success, a MDB2 error on failure
* @access public
*/
function listTables()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
// gratuitously stolen from PEAR DB _getSpecialQuery in pgsql.php
$query = '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_'";
$result = $db->queryCol($query);
if (MDB2::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableFields()
/**
* list all fields in a table in the current database
*
* @param string $table name of table that should be used in method
* @return mixed array of field names on success, a MDB2 error on failure
* @access public
*/
function listTableFields($table)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table);
$table = $db->quoteIdentifier($table, true);
if (!empty($schema)) {
$table = $db->quoteIdentifier($schema, true) . '.' .$table;
}
$db->setLimit(1);
$result2 = $db->query("SELECT * FROM $table");
if (MDB2::isError($result2)) {
return $result2;
}
$result = $result2->getColumnNames();
$result2->free();
if (MDB2::isError($result)) {
return $result;
}
return array_flip($result);
}
// }}}
// {{{ listTableIndexes()
/**
* list all indexes in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of index names on success, a MDB2 error on failure
* @access public
*/
function listTableIndexes($table)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table);
$table = $db->quote($table, 'text');
$subquery = "SELECT indexrelid
FROM pg_index
LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
WHERE pg_class.relname = $table
AND indisunique != 't'
AND indisprimary != 't'";
if (!empty($schema)) {
$subquery .= ' AND pg_namespace.nspname = '.$db->quote($schema, 'text');
}
$query = "SELECT relname FROM pg_class WHERE oid IN ($subquery)";
$indexes = $db->queryCol($query, 'text');
if (MDB2::isError($indexes)) {
return $indexes;
}
$result = array();
foreach ($indexes as $index) {
$index = $this->_fixIndexName($index);
if (!empty($index)) {
$result[$index] = true;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ dropConstraint()
/**
* drop existing constraint
*
* @param string $table name of table that should be used in method
* @param string $name name of the constraint to be dropped
* @param string $primary hint if the constraint is primary
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropConstraint($table, $name, $primary = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
// is it an UNIQUE index?
$query = 'SELECT relname
FROM pg_class
WHERE oid IN (
SELECT indexrelid
FROM pg_index, pg_class
WHERE pg_class.relname = '.$db->quote($table, 'text').'
AND pg_class.oid = pg_index.indrelid
AND indisunique = \'t\')
EXCEPT
SELECT conname
FROM pg_constraint, pg_class
WHERE pg_constraint.conrelid = pg_class.oid
AND relname = '. $db->quote($table, 'text');
$unique = $db->queryCol($query, 'text');
if (MDB2::isError($unique) || empty($unique)) {
// not an UNIQUE index, maybe a CONSTRAINT
return parent::dropConstraint($table, $name, $primary);
}
if (in_array($name, $unique)) {
$result = $db->exec('DROP INDEX '.$db->quoteIdentifier($name, true));
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
$idxname = $db->getIndexName($name);
if (in_array($idxname, $unique)) {
$result = $db->exec('DROP INDEX '.$db->quoteIdentifier($idxname, true));
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$name . ' is not an existing constraint for table ' . $table, __FUNCTION__);
}
// }}}
// {{{ listTableConstraints()
/**
* list all constraints in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of constraint names on success, a MDB2 error on failure
* @access public
*/
function listTableConstraints($table)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table);
$table = $db->quote($table, 'text');
$query = 'SELECT conname
FROM pg_constraint
LEFT JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
WHERE relname = ' .$table;
if (!empty($schema)) {
$query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
}
$query .= '
UNION DISTINCT
SELECT relname
FROM pg_class
WHERE oid IN (
SELECT indexrelid
FROM pg_index
LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
WHERE pg_class.relname = '.$table.'
AND indisunique = \'t\'';
if (!empty($schema)) {
$query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
}
$query .= ')';
$constraints = $db->queryCol($query);
if (MDB2::isError($constraints)) {
return $constraints;
}
$result = array();
foreach ($constraints as $constraint) {
$constraint = $this->_fixIndexName($constraint);
if (!empty($constraint)) {
$result[$constraint] = true;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
&& $db->options['field_case'] == CASE_LOWER
) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ createSequence()
/**
* create sequence
*
* @param string $seq_name name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createSequence($seq_name, $start = 1)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
$result = $db->exec("CREATE SEQUENCE $sequence_name INCREMENT 1".
($start < 1 ? " MINVALUE $start" : '')." START $start");
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ dropSequence()
/**
* drop existing sequence
*
* @param string $seq_name name of the sequence to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropSequence($seq_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
$result = $db->exec("DROP SEQUENCE $sequence_name");
if (MDB2::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ listSequences()
/**
* list all sequences in the current database
*
* @return mixed array of sequence names on success, a MDB2 error on failure
* @access public
*/
function listSequences()
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT relname FROM pg_class WHERE relkind = 'S' AND relnamespace IN";
$query.= "(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
$table_names = $db->queryCol($query);
if (MDB2::isError($table_names)) {
return $table_names;
}
$result = array();
foreach ($table_names as $table_name) {
$result[] = $this->_fixSequenceName($table_name);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
}
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
* Base class for the natuve modules that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Native');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Native_Common extends MDB2_Module_Common
{
}
?>

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 FrontBase driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
*/
class MDB2_Driver_Native_fbsql extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 FireBird/InterBase driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
*/
class MDB2_Driver_Native_ibase extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 MSSQL driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
*/
class MDB2_Driver_Native_mssql extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 MySQLi driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Native_mysqli extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 Oracle driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
*/
class MDB2_Driver_Native_oci8 extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 PostGreSQL driver for the native module
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Driver_Native_pgsql extends MDB2_Driver_Native_Common
{
// }}}
// {{{ deleteOID()
/**
* delete an OID
*
* @param integer $OID
* @return mixed MDB2_OK on success or MDB2 Error Object on failure
* @access public
*/
function deleteOID($OID)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$connection = $db->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
if (!@pg_lo_unlink($connection, $OID)) {
return $db->raiseError(null, null, null,
'Unable to unlink OID: '.$OID, __FUNCTION__);
}
return MDB2_OK;
}
}
?>

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 SQLite driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Native_sqlite extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 SQLite driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Native_sqlite3 extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,57 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 MSSQL driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
*/
class MDB2_Driver_Native_sqlsrv extends MDB2_Driver_Native_Common
{
}
?>

View File

@ -0,0 +1,517 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
* @package MDB2
* @category Database
*/
/**
* These are constants for the tableInfo-function
* they are bitwised or'ed. so if there are more constants to be defined
* in the future, adjust MDB2_TABLEINFO_FULL accordingly
*/
define('MDB2_TABLEINFO_ORDER', 1);
define('MDB2_TABLEINFO_ORDERTABLE', 2);
define('MDB2_TABLEINFO_FULL', 3);
/**
* Base class for the schema reverse engineering module that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Reverse');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Reverse_Common extends MDB2_Module_Common
{
// {{{ splitTableSchema()
/**
* Split the "[owner|schema].table" notation into an array
*
* @param string $table [schema and] table name
*
* @return array array(schema, table)
* @access private
*/
function splitTableSchema($table)
{
$ret = array();
if (strpos($table, '.') !== false) {
return explode('.', $table);
}
return array(null, $table);
}
// }}}
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table name of table that should be used in method
* @param string $field name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure.
* The returned array contains an array for each field definition,
* with all or some of these indices, depending on the field data type:
* [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type]
* @access public
*/
function getTableFieldDefinition($table, $field)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table name of table that should be used in method
* @param string $index name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* </pre>
* array (
* [fields] => array (
* [field1name] => array() // one entry per each field covered
* [field2name] => array() // by the index
* [field3name] => array(
* [sorting] => ascending
* )
* )
* );
* </pre>
* @access public
*/
function getTableIndexDefinition($table, $index)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of an constraints into an array
*
* @param string $table name of table that should be used in method
* @param string $index name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* <pre>
* array (
* [primary] => 0
* [unique] => 0
* [foreign] => 1
* [check] => 0
* [fields] => array (
* [field1name] => array() // one entry per each field covered
* [field2name] => array() // by the index
* [field3name] => array(
* [sorting] => ascending
* [position] => 3
* )
* )
* [references] => array(
* [table] => name
* [fields] => array(
* [field1name] => array( //one entry per each referenced field
* [position] => 1
* )
* )
* )
* [deferrable] => 0
* [initiallydeferred] => 0
* [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
* [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
* [match] => SIMPLE|PARTIAL|FULL
* );
* </pre>
* @access public
*/
function getTableConstraintDefinition($table, $index)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ getSequenceDefinition()
/**
* Get the structure of a sequence into an array
*
* @param string $sequence name of sequence that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* <pre>
* array (
* [start] => n
* );
* </pre>
* @access public
*/
function getSequenceDefinition($sequence)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$start = $db->currId($sequence);
if (MDB2::isError($start)) {
return $start;
}
if ($db->supports('current_id')) {
$start++;
} else {
$db->warnings[] = 'database does not support getting current
sequence value, the sequence value was incremented';
}
$definition = array();
if ($start != 1) {
$definition = array('start' => $start);
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* <pre>
* array (
* [trigger_name] => 'trigger name',
* [table_name] => 'table name',
* [trigger_body] => 'trigger body definition',
* [trigger_type] => 'BEFORE' | 'AFTER',
* [trigger_event] => 'INSERT' | 'UPDATE' | 'DELETE'
* //or comma separated list of multiple events, when supported
* [trigger_enabled] => true|false
* [trigger_comment] => 'trigger comment',
* );
* </pre>
* The oci8 driver also returns a [when_clause] index.
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ tableInfo()
/**
* Returns information about a table or a result set
*
* The format of the resulting array depends on which <var>$mode</var>
* you select. The sample output below is based on this query:
* <pre>
* SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
* FROM tblFoo
* JOIN tblBar ON tblFoo.fldId = tblBar.fldId
* </pre>
*
* <ul>
* <li>
*
* <kbd>null</kbd> (default)
* <pre>
* [0] => Array (
* [table] => tblFoo
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* [1] => Array (
* [table] => tblFoo
* [name] => fldPhone
* [type] => string
* [len] => 20
* [flags] =>
* )
* [2] => Array (
* [table] => tblBar
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* </pre>
*
* </li><li>
*
* <kbd>MDB2_TABLEINFO_ORDER</kbd>
*
* <p>In addition to the information found in the default output,
* a notation of the number of columns is provided by the
* <samp>num_fields</samp> element while the <samp>order</samp>
* element provides an array with the column names as the keys and
* their location index number (corresponding to the keys in the
* the default output) as the values.</p>
*
* <p>If a result set has identical field names, the last one is
* used.</p>
*
* <pre>
* [num_fields] => 3
* [order] => Array (
* [fldId] => 2
* [fldTrans] => 1
* )
* </pre>
*
* </li><li>
*
* <kbd>MDB2_TABLEINFO_ORDERTABLE</kbd>
*
* <p>Similar to <kbd>MDB2_TABLEINFO_ORDER</kbd> but adds more
* dimensions to the array in which the table names are keys and
* the field names are sub-keys. This is helpful for queries that
* join tables which have identical field names.</p>
*
* <pre>
* [num_fields] => 3
* [ordertable] => Array (
* [tblFoo] => Array (
* [fldId] => 0
* [fldPhone] => 1
* )
* [tblBar] => Array (
* [fldId] => 2
* )
* )
* </pre>
*
* </li>
* </ul>
*
* The <samp>flags</samp> element contains a space separated list
* of extra information about the field. This data is inconsistent
* between DBMS's due to the way each DBMS works.
* + <samp>primary_key</samp>
* + <samp>unique_key</samp>
* + <samp>multiple_key</samp>
* + <samp>not_null</samp>
*
* Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
* elements if <var>$result</var> is a table name. The following DBMS's
* provide full information from queries:
* + fbsql
* + mysql
*
* If the 'portability' option has <samp>MDB2_PORTABILITY_FIX_CASE</samp>
* turned on, the names of tables and fields will be lower or upper cased.
*
* @param object|string $result MDB2_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 either unused or one of the tableInfo modes:
* <kbd>MDB2_TABLEINFO_ORDERTABLE</kbd>,
* <kbd>MDB2_TABLEINFO_ORDER</kbd> or
* <kbd>MDB2_TABLEINFO_FULL</kbd> (which does both).
* These are bitwise, so the first two can be
* combined using <kbd>|</kbd>.
*
* @return array an associative array with the information requested.
* A MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::setOption()
*/
function tableInfo($result, $mode = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if (!is_string($result)) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
$db->loadModule('Manager', null, true);
$fields = $db->manager->listTableFields($result);
if (MDB2::isError($fields)) {
return $fields;
}
$flags = array();
$idxname_format = $db->getOption('idxname_format');
$db->setOption('idxname_format', '%s');
$indexes = $db->manager->listTableIndexes($result);
if (MDB2::isError($indexes)) {
$db->setOption('idxname_format', $idxname_format);
return $indexes;
}
foreach ($indexes as $index) {
$definition = $this->getTableIndexDefinition($result, $index);
if (MDB2::isError($definition)) {
$db->setOption('idxname_format', $idxname_format);
return $definition;
}
if (count($definition['fields']) > 1) {
foreach ($definition['fields'] as $field => $sort) {
$flags[$field] = 'multiple_key';
}
}
}
$constraints = $db->manager->listTableConstraints($result);
if (MDB2::isError($constraints)) {
return $constraints;
}
foreach ($constraints as $constraint) {
$definition = $this->getTableConstraintDefinition($result, $constraint);
if (MDB2::isError($definition)) {
$db->setOption('idxname_format', $idxname_format);
return $definition;
}
$flag = !empty($definition['primary'])
? 'primary_key' : (!empty($definition['unique'])
? 'unique_key' : false);
if ($flag) {
foreach ($definition['fields'] as $field => $sort) {
if (empty($flags[$field]) || $flags[$field] != 'primary_key') {
$flags[$field] = $flag;
}
}
}
}
$res = array();
if ($mode) {
$res['num_fields'] = count($fields);
}
foreach ($fields as $i => $field) {
$definition = $this->getTableFieldDefinition($result, $field);
if (MDB2::isError($definition)) {
$db->setOption('idxname_format', $idxname_format);
return $definition;
}
$res[$i] = $definition[0];
$res[$i]['name'] = $field;
$res[$i]['table'] = $result;
$res[$i]['type'] = preg_replace('/^([a-z]+).*$/i', '\\1', trim($definition[0]['nativetype']));
// 'primary_key', 'unique_key', 'multiple_key'
$res[$i]['flags'] = empty($flags[$field]) ? '' : $flags[$field];
// not_null', 'unsigned', 'auto_increment', 'default_[rawencodedvalue]'
if (!empty($res[$i]['notnull'])) {
$res[$i]['flags'].= ' not_null';
}
if (!empty($res[$i]['unsigned'])) {
$res[$i]['flags'].= ' unsigned';
}
if (!empty($res[$i]['auto_increment'])) {
$res[$i]['flags'].= ' autoincrement';
}
if (!empty($res[$i]['default'])) {
$res[$i]['flags'].= ' default_'.rawurlencode($res[$i]['default']);
}
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
$db->setOption('idxname_format', $idxname_format);
return $res;
}
}
?>

View File

@ -0,0 +1,132 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 FrontBase driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
*/
class MDB2_Driver_Reverse_fbsql extends MDB2_Driver_Reverse_Common
{
// }}}
// {{{ tableInfo()
/**
* Returns information about a table or a result set
*
* @param object|string $result MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @fbsql_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => $case_func(@fbsql_field_table($resource, $i)),
'name' => $case_func(@fbsql_field_name($resource, $i)),
'type' => @fbsql_field_type($resource, $i),
'length' => @fbsql_field_len($resource, $i),
'flags' => @fbsql_field_flags($resource, $i),
);
// todo: implement $db->datatype->mapNativeDatatype();
$res[$i]['mdb2type'] = $res[$i]['type'];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
return $res;
}
}
?>

View File

@ -0,0 +1,590 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 InterbaseBase driver for the reverse engineering module
*
* @package MDB2
* @category Database
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_ibase extends MDB2_Driver_Reverse_Common
{
/**
* Array for converting constant values to text values
* @var array
* @access public
*/
var $types = array(
7 => 'smallint',
8 => 'integer',
9 => 'quad',
10 => 'float',
11 => 'd_float',
12 => 'date', //dialect 3 DATE
13 => 'time',
14 => 'char',
16 => 'int64',
27 => 'double',
35 => 'timestamp', //DATE in older versions
37 => 'varchar',
40 => 'cstring',
261 => 'blob',
);
/**
* Array for converting constant values to text values
* @var array
* @access public
*/
var $subtypes = array(
//char subtypes
14 => array(
0 => 'unspecified',
1 => 'fixed', //BINARY data
),
//blob subtypes
261 => array(
0 => 'unspecified',
1 => 'text',
2 => 'BLR', //Binary Language Representation
3 => 'access control list',
4 => 'reserved for future use',
5 => 'encoded description of a table\'s current metadata',
6 => 'description of multi-database transaction that finished irregularly',
),
//smallint subtypes
7 => array(
0 => 'RDB$FIELD_TYPE',
1 => 'numeric',
2 => 'decimal',
),
//integer subtypes
8 => array(
0 => 'RDB$FIELD_TYPE',
1 => 'numeric',
2 => 'decimal',
),
//int64 subtypes
16 => array(
0 => 'RDB$FIELD_TYPE',
1 => 'numeric',
2 => 'decimal',
),
);
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quote(strtoupper($table), 'text');
$field_name = $db->quote(strtoupper($field_name), 'text');
$query = "SELECT RDB\$RELATION_FIELDS.RDB\$FIELD_NAME AS name,
RDB\$FIELDS.RDB\$FIELD_LENGTH AS \"length\",
RDB\$FIELDS.RDB\$FIELD_PRECISION AS \"precision\",
(RDB\$FIELDS.RDB\$FIELD_SCALE * -1) AS \"scale\",
RDB\$FIELDS.RDB\$FIELD_TYPE AS field_type_code,
RDB\$FIELDS.RDB\$FIELD_SUB_TYPE AS field_sub_type_code,
RDB\$RELATION_FIELDS.RDB\$DESCRIPTION AS description,
RDB\$RELATION_FIELDS.RDB\$NULL_FLAG AS null_flag,
RDB\$FIELDS.RDB\$DEFAULT_SOURCE AS default_source,
RDB\$CHARACTER_SETS.RDB\$CHARACTER_SET_NAME AS \"charset\",
RDB\$COLLATIONS.RDB\$COLLATION_NAME AS \"collation\"
FROM RDB\$FIELDS
LEFT JOIN RDB\$RELATION_FIELDS ON RDB\$FIELDS.RDB\$FIELD_NAME = RDB\$RELATION_FIELDS.RDB\$FIELD_SOURCE
LEFT JOIN RDB\$CHARACTER_SETS ON RDB\$FIELDS.RDB\$CHARACTER_SET_ID = RDB\$CHARACTER_SETS.RDB\$CHARACTER_SET_ID
LEFT JOIN RDB\$COLLATIONS ON RDB\$FIELDS.RDB\$COLLATION_ID = RDB\$COLLATIONS.RDB\$COLLATION_ID
WHERE UPPER(RDB\$RELATION_FIELDS.RDB\$RELATION_NAME)=$table
AND UPPER(RDB\$RELATION_FIELDS.RDB\$FIELD_NAME)=$field_name;";
$column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($column)) {
return $column;
}
if (empty($column)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
$column = array_change_key_case($column, CASE_LOWER);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
}
$column['type'] = array_key_exists((int)$column['field_type_code'], $this->types)
? $this->types[(int)$column['field_type_code']] : 'undefined';
if ($column['field_sub_type_code']
&& array_key_exists((int)$column['field_type_code'], $this->subtypes)
&& array_key_exists($column['field_sub_type_code'], $this->subtypes[(int)$column['field_type_code']])
) {
$column['field_sub_type'] = $this->subtypes[(int)$column['field_type_code']][$column['field_sub_type_code']];
} else {
$column['field_sub_type'] = null;
}
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = !empty($column['null_flag']);
$default = $column['default_source'];
if ((null === $default) && $notnull) {
$default = ($types[0] == 'integer') ? 0 : '';
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => $column['type'],
'charset' => $column['charset'],
'collation' => $column['collation'],
);
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if (false !== $default) {
$definition[0]['default'] = $default;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name, $format_index_name = true)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quote(strtoupper($table), 'text');
$query = "SELECT RDB\$INDEX_SEGMENTS.RDB\$FIELD_NAME AS field_name,
RDB\$INDICES.RDB\$DESCRIPTION AS description,
(RDB\$INDEX_SEGMENTS.RDB\$FIELD_POSITION + 1) AS field_position
FROM RDB\$INDEX_SEGMENTS
LEFT JOIN RDB\$INDICES ON RDB\$INDICES.RDB\$INDEX_NAME = RDB\$INDEX_SEGMENTS.RDB\$INDEX_NAME
LEFT JOIN RDB\$RELATION_CONSTRAINTS ON RDB\$RELATION_CONSTRAINTS.RDB\$INDEX_NAME = RDB\$INDEX_SEGMENTS.RDB\$INDEX_NAME
WHERE UPPER(RDB\$INDICES.RDB\$RELATION_NAME)=$table
AND UPPER(RDB\$INDICES.RDB\$INDEX_NAME)=%s
AND RDB\$RELATION_CONSTRAINTS.RDB\$CONSTRAINT_TYPE IS NULL
ORDER BY RDB\$INDEX_SEGMENTS.RDB\$FIELD_POSITION";
$index_name_mdb2 = $db->quote(strtoupper($db->getIndexName($index_name)), 'text');
$result = $db->queryRow(sprintf($query, $index_name_mdb2));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$index_name = $index_name_mdb2;
} else {
$index_name = $db->quote(strtoupper($index_name), 'text');
}
$result = $db->query(sprintf($query, $index_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array();
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['field_position'],
);
/*
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A'
? 'ascending' : 'descending');
}
*/
}
$result->free();
if (empty($definition)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quote(strtoupper($table), 'text');
$query = "SELECT rc.RDB\$CONSTRAINT_NAME,
s.RDB\$FIELD_NAME AS field_name,
CASE WHEN rc.RDB\$CONSTRAINT_TYPE = 'PRIMARY KEY' THEN 1 ELSE 0 END AS \"primary\",
CASE WHEN rc.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY' THEN 1 ELSE 0 END AS \"foreign\",
CASE WHEN rc.RDB\$CONSTRAINT_TYPE = 'UNIQUE' THEN 1 ELSE 0 END AS \"unique\",
CASE WHEN rc.RDB\$CONSTRAINT_TYPE = 'CHECK' THEN 1 ELSE 0 END AS \"check\",
i.RDB\$DESCRIPTION AS description,
CASE WHEN rc.RDB\$DEFERRABLE = 'NO' THEN 0 ELSE 1 END AS deferrable,
CASE WHEN rc.RDB\$INITIALLY_DEFERRED = 'NO' THEN 0 ELSE 1 END AS initiallydeferred,
refc.RDB\$UPDATE_RULE AS onupdate,
refc.RDB\$DELETE_RULE AS ondelete,
refc.RDB\$MATCH_OPTION AS \"match\",
i2.RDB\$RELATION_NAME AS references_table,
s2.RDB\$FIELD_NAME AS references_field,
(s.RDB\$FIELD_POSITION + 1) AS field_position
FROM RDB\$INDEX_SEGMENTS s
LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
LEFT JOIN RDB\$RELATION_CONSTRAINTS rc ON rc.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
LEFT JOIN RDB\$REF_CONSTRAINTS refc ON rc.RDB\$CONSTRAINT_NAME = refc.RDB\$CONSTRAINT_NAME
LEFT JOIN RDB\$RELATION_CONSTRAINTS rc2 ON rc2.RDB\$CONSTRAINT_NAME = refc.RDB\$CONST_NAME_UQ
LEFT JOIN RDB\$INDICES i2 ON i2.RDB\$INDEX_NAME = rc2.RDB\$INDEX_NAME
LEFT JOIN RDB\$INDEX_SEGMENTS s2 ON i2.RDB\$INDEX_NAME = s2.RDB\$INDEX_NAME
AND s.RDB\$FIELD_POSITION = s2.RDB\$FIELD_POSITION
WHERE UPPER(i.RDB\$RELATION_NAME)=$table
AND UPPER(rc.RDB\$CONSTRAINT_NAME)=%s
AND rc.RDB\$CONSTRAINT_TYPE IS NOT NULL
ORDER BY s.RDB\$FIELD_POSITION";
$constraint_name_mdb2 = $db->quote(strtoupper($db->getIndexName($constraint_name)), 'text');
$result = $db->queryRow(sprintf($query, $constraint_name_mdb2));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$constraint_name = $constraint_name_mdb2;
} else {
$constraint_name = $db->quote(strtoupper($constraint_name), 'text');
}
$result = $db->query(sprintf($query, $constraint_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array();
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['field_position']
);
if ($row['foreign']) {
$ref_column_name = $row['references_field'];
$ref_table_name = $row['references_table'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$ref_column_name = strtolower($ref_column_name);
$ref_table_name = strtolower($ref_table_name);
} else {
$ref_column_name = strtoupper($ref_column_name);
$ref_table_name = strtoupper($ref_table_name);
}
}
$definition['references']['table'] = $ref_table_name;
$definition['references']['fields'][$ref_column_name] = array(
'position' => (int)$row['field_position']
);
}
//collation?!?
/*
if (!empty($row['collation'])) {
$definition['fields'][$field]['sorting'] = ($row['collation'] == 'A'
? 'ascending' : 'descending');
}
*/
$lastrow = $row;
// otherwise $row is no longer usable on exit from loop
}
$result->free();
if (empty($definition)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$definition['primary'] = (boolean)$lastrow['primary'];
$definition['unique'] = (boolean)$lastrow['unique'];
$definition['foreign'] = (boolean)$lastrow['foreign'];
$definition['check'] = (boolean)$lastrow['check'];
$definition['deferrable'] = (boolean)$lastrow['deferrable'];
$definition['initiallydeferred'] = (boolean)$lastrow['initiallydeferred'];
$definition['onupdate'] = $lastrow['onupdate'];
$definition['ondelete'] = $lastrow['ondelete'];
$definition['match'] = $lastrow['match'];
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$trigger = $db->quote(strtoupper($trigger), 'text');
$query = "SELECT RDB\$TRIGGER_NAME AS trigger_name,
RDB\$RELATION_NAME AS table_name,
RDB\$TRIGGER_SOURCE AS trigger_body,
CASE RDB\$TRIGGER_TYPE
WHEN 1 THEN 'BEFORE'
WHEN 2 THEN 'AFTER'
WHEN 3 THEN 'BEFORE'
WHEN 4 THEN 'AFTER'
WHEN 5 THEN 'BEFORE'
WHEN 6 THEN 'AFTER'
END AS trigger_type,
CASE RDB\$TRIGGER_TYPE
WHEN 1 THEN 'INSERT'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'UPDATE'
WHEN 4 THEN 'UPDATE'
WHEN 5 THEN 'DELETE'
WHEN 6 THEN 'DELETE'
END AS trigger_event,
CASE RDB\$TRIGGER_INACTIVE
WHEN 1 THEN 0 ELSE 1
END AS trigger_enabled,
RDB\$DESCRIPTION AS trigger_comment
FROM RDB\$TRIGGERS
WHERE UPPER(RDB\$TRIGGER_NAME)=$trigger";
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'clob',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($def)) {
return $def;
}
$clob = $def['trigger_body'];
if (!MDB2::isError($clob) && is_resource($clob)) {
$value = '';
while (!feof($clob)) {
$data = fread($clob, 8192);
$value.= $data;
}
$db->datatype->destroyLOB($clob);
$def['trigger_body'] = $value;
}
return $def;
}
// }}}
// {{{ 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 MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @ibase_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$info = @ibase_field_info($resource, $i);
if (($pos = strpos($info['type'], '(')) !== false) {
$info['type'] = substr($info['type'], 0, $pos);
}
$res[$i] = array(
'table' => $case_func($info['relation']),
'name' => $case_func($info['name']),
'type' => $info['type'],
'length' => $info['length'],
'flags' => '',
);
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (MDB2::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
return $res;
}
}
?>

View File

@ -0,0 +1,653 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 MSSQL driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_mssql extends MDB2_Driver_Reverse_Common
{
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$fldname = $db->quoteIdentifier($field_name, true);
$query = "SELECT t.table_name,
c.column_name 'name',
c.data_type 'type',
CASE c.is_nullable WHEN 'YES' THEN 1 ELSE 0 END AS 'is_nullable',
c.column_default,
c.character_maximum_length 'length',
c.numeric_precision,
c.numeric_scale,
c.character_set_name,
c.collation_name
FROM INFORMATION_SCHEMA.TABLES t,
INFORMATION_SCHEMA.COLUMNS c
WHERE t.table_name = c.table_name
AND t.table_name = '$table'
AND c.column_name = '$fldname'";
if (!empty($schema)) {
$query .= " AND t.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ' ORDER BY t.table_name';
$column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($column)) {
return $column;
}
if (empty($column)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = true;
if ($column['is_nullable']) {
$notnull = false;
}
$default = false;
if (array_key_exists('column_default', $column)) {
$default = $column['column_default'];
if ((null === $default) && $notnull) {
$default = '';
} elseif (strlen($default) > 4
&& substr($default, 0, 1) == '('
&& substr($default, -1, 1) == ')'
) {
//mssql wraps the default value in parentheses: "((1234))", "(NULL)"
$default = trim($default, '()');
if ($default == 'NULL') {
$default = null;
}
}
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if (false !== $default) {
$definition[0]['default'] = $default;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
//$idxname = $db->quoteIdentifier($index_name, true);
$query = "SELECT OBJECT_NAME(i.id) tablename,
i.name indexname,
c.name field_name,
CASE INDEXKEY_PROPERTY(i.id, i.indid, ik.keyno, 'IsDescending')
WHEN 1 THEN 'DESC' ELSE 'ASC'
END 'collation',
ik.keyno 'position'
FROM sysindexes i
JOIN sysindexkeys ik ON ik.id = i.id AND ik.indid = i.indid
JOIN syscolumns c ON c.id = ik.id AND c.colid = ik.colid
WHERE OBJECT_NAME(i.id) = '$table'
AND i.name = '%s'
AND NOT EXISTS (
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
WHERE k.table_name = OBJECT_NAME(i.id)
AND k.constraint_name = i.name";
if (!empty($schema)) {
$query .= " AND k.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ')
ORDER BY tablename, indexname, ik.keyno';
$index_name_mdb2 = $db->getIndexName($index_name);
$result = $db->queryRow(sprintf($query, $index_name_mdb2));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$index_name = $index_name_mdb2;
}
$result = $db->query(sprintf($query, $index_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array();
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['position'],
);
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'ASC'
? 'ascending' : 'descending');
}
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$query = "SELECT k.table_name,
k.column_name field_name,
CASE c.constraint_type WHEN 'PRIMARY KEY' THEN 1 ELSE 0 END 'primary',
CASE c.constraint_type WHEN 'UNIQUE' THEN 1 ELSE 0 END 'unique',
CASE c.constraint_type WHEN 'FOREIGN KEY' THEN 1 ELSE 0 END 'foreign',
CASE c.constraint_type WHEN 'CHECK' THEN 1 ELSE 0 END 'check',
CASE c.is_deferrable WHEN 'NO' THEN 0 ELSE 1 END 'deferrable',
CASE c.initially_deferred WHEN 'NO' THEN 0 ELSE 1 END 'initiallydeferred',
rc.match_option 'match',
rc.update_rule 'onupdate',
rc.delete_rule 'ondelete',
kcu.table_name 'references_table',
kcu.column_name 'references_field',
k.ordinal_position 'field_position'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS c
ON k.table_name = c.table_name
AND k.table_schema = c.table_schema
AND k.table_catalog = c.table_catalog
AND k.constraint_catalog = c.constraint_catalog
AND k.constraint_name = c.constraint_name
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
ON rc.constraint_schema = c.constraint_schema
AND rc.constraint_catalog = c.constraint_catalog
AND rc.constraint_name = c.constraint_name
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
ON rc.unique_constraint_schema = kcu.constraint_schema
AND rc.unique_constraint_catalog = kcu.constraint_catalog
AND rc.unique_constraint_name = kcu.constraint_name
AND k.ordinal_position = kcu.ordinal_position
WHERE k.constraint_catalog = DB_NAME()
AND k.table_name = '$table'
AND k.constraint_name = '%s'";
if (!empty($schema)) {
$query .= " AND k.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ' ORDER BY k.constraint_name,
k.ordinal_position';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$result = $db->queryRow(sprintf($query, $constraint_name_mdb2));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$constraint_name = $constraint_name_mdb2;
}
$result = $db->query(sprintf($query, $constraint_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array(
'fields' => array()
);
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['field_position']
);
if ($row['foreign']) {
$ref_column_name = $row['references_field'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$ref_column_name = strtolower($ref_column_name);
} else {
$ref_column_name = strtoupper($ref_column_name);
}
}
$definition['references']['table'] = $row['references_table'];
$definition['references']['fields'][$ref_column_name] = array(
'position' => (int)$row['field_position']
);
}
//collation?!?
/*
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'ASC'
? 'ascending' : 'descending');
}
*/
$lastrow = $row;
// otherwise $row is no longer usable on exit from loop
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$definition['primary'] = (boolean)$lastrow['primary'];
$definition['unique'] = (boolean)$lastrow['unique'];
$definition['foreign'] = (boolean)$lastrow['foreign'];
$definition['check'] = (boolean)$lastrow['check'];
$definition['deferrable'] = (boolean)$lastrow['deferrable'];
$definition['initiallydeferred'] = (boolean)$lastrow['initiallydeferred'];
$definition['onupdate'] = $lastrow['onupdate'];
$definition['ondelete'] = $lastrow['ondelete'];
$definition['match'] = $lastrow['match'];
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT sys1.name trigger_name,
sys2.name table_name,
c.text trigger_body,
c.encrypted is_encripted,
CASE
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsTriggerDisabled') = 1
THEN 0 ELSE 1
END trigger_enabled,
CASE
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsInsertTrigger') = 1
THEN 'INSERT'
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsUpdateTrigger') = 1
THEN 'UPDATE'
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsDeleteTrigger') = 1
THEN 'DELETE'
END trigger_event,
CASE WHEN OBJECTPROPERTY(sys1.id, 'ExecIsInsteadOfTrigger') = 1
THEN 'INSTEAD OF' ELSE 'AFTER'
END trigger_type,
'' trigger_comment
FROM sysobjects sys1
JOIN sysobjects sys2 ON sys1.parent_obj = sys2.id
JOIN syscomments c ON sys1.id = c.id
WHERE sys1.xtype = 'TR'
AND sys1.name = ". $db->quote($trigger, 'text');
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
'is_encripted' => 'boolean',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($def)) {
return $def;
}
$trg_body = $db->queryCol('EXEC sp_helptext '. $db->quote($trigger, 'text'), 'text');
if (!MDB2::isError($trg_body)) {
$def['trigger_body'] = implode(' ', $trg_body);
}
return $def;
}
// }}}
// {{{ 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 MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @mssql_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => '',
'name' => $case_func(@mssql_field_name($resource, $i)),
'type' => @mssql_field_type($resource, $i),
'length' => @mssql_field_length($resource, $i),
'flags' => '',
);
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (MDB2::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
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 protected
* @author Joern Barthel <j_barthel@web.de>
*/
function _mssql_field_flags($table, $column)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
static $tableName = null;
static $flags = array();
if ($table != $tableName) {
$flags = array();
$tableName = $table;
// get unique and primary keys
$res = $db->queryAll("EXEC SP_HELPINDEX[$table]", null, MDB2_FETCHMODE_ASSOC);
foreach ($res as $val) {
$val = array_change_key_case($val, CASE_LOWER);
$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 = $db->queryAll("EXEC SP_COLUMNS[$table]", null, MDB2_FETCHMODE_ASSOC);
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 (!empty($flags[$column])) {
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 protected
* @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);
}
}
// }}}
}
?>

View File

@ -0,0 +1,610 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 MySQLi driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_mysqli extends MDB2_Driver_Reverse_Common
{
/**
* Array for converting MYSQLI_*_FLAG constants to text values
* @var array
* @access public
*/
var $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
*/
var $types = array(
MYSQLI_TYPE_DECIMAL => 'decimal',
246 => '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',
);
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$query = "SHOW FULL COLUMNS FROM $table LIKE ".$db->quote($field_name);
$columns = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($columns)) {
return $columns;
}
foreach ($columns as $column) {
$column = array_change_key_case($column, CASE_LOWER);
$column['name'] = $column['field'];
unset($column['field']);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
if ($field_name == $column['name']) {
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (empty($column['null']) || $column['null'] !== 'YES') {
$notnull = true;
}
$default = false;
if (array_key_exists('default', $column)) {
$default = $column['default'];
if ((null === $default) && $notnull) {
$default = '';
}
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
$autoincrement = false;
if (!empty($column['extra'])) {
if ($column['extra'] == 'auto_increment') {
$autoincrement = true;
} else {
$definition[0]['extra'] = $column['extra'];
}
}
$collate = null;
if (!empty($column['collation'])) {
$collate = $column['collation'];
$charset = preg_replace('/(.+?)(_.+)?/', '$1', $collate);
}
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
if ($autoincrement !== false) {
$definition[0]['autoincrement'] = $autoincrement;
}
if (null !== $collate) {
$definition[0]['collate'] = $collate;
$definition[0]['charset'] = $charset;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
} elseif ($type == 'timestamp' && $notnull && empty($definition[$key]['default'])) {
$definition[$key]['default'] = '0000-00-00 00:00:00';
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */";
$index_name_mdb2 = $db->getIndexName($index_name);
$result = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2)));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$index_name = $index_name_mdb2;
}
$result = $db->query(sprintf($query, $db->quote($index_name)));
if (MDB2::isError($result)) {
return $result;
}
$colpos = 1;
$definition = array();
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$key_name = $row['key_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$key_name = strtolower($key_name);
} else {
$key_name = strtoupper($key_name);
}
}
if ($index_name == $key_name) {
if (!$row['non_unique']) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$index_name . ' is not an existing table index', __FUNCTION__);
}
$column_name = $row['column_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => $colpos++
);
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A'
? 'ascending' : 'descending');
}
}
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$index_name . ' is not an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$constraint_name_original = $constraint_name;
$table = $db->quoteIdentifier($table, true);
$query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */";
if (strtolower($constraint_name) != 'primary') {
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$result = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2)));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$constraint_name = $constraint_name_mdb2;
}
}
$result = $db->query(sprintf($query, $db->quote($constraint_name)));
if (MDB2::isError($result)) {
return $result;
}
$colpos = 1;
//default values, eventually overridden
$definition = array(
'primary' => false,
'unique' => false,
'foreign' => false,
'check' => false,
'fields' => array(),
'references' => array(
'table' => '',
'fields' => array(),
),
'onupdate' => '',
'ondelete' => '',
'match' => '',
'deferrable' => false,
'initiallydeferred' => false,
);
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$key_name = $row['key_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$key_name = strtolower($key_name);
} else {
$key_name = strtoupper($key_name);
}
}
if ($constraint_name == $key_name) {
if ($row['non_unique']) {
//FOREIGN KEY?
return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition);
}
if ($row['key_name'] == 'PRIMARY') {
$definition['primary'] = true;
} elseif (!$row['non_unique']) {
$definition['unique'] = true;
}
$column_name = $row['column_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => $colpos++
);
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A'
? 'ascending' : 'descending');
}
}
}
$result->free();
if (empty($definition['fields'])) {
return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition);
}
return $definition;
}
// }}}
// {{{ _getTableFKConstraintDefinition()
/**
* Get the FK definition from the CREATE TABLE statement
*
* @param string $table table name
* @param string $constraint_name constraint name
* @param array $definition default values for constraint definition
*
* @return array|PEAR_Error
* @access private
*/
function _getTableFKConstraintDefinition($table, $constraint_name, $definition)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
//Use INFORMATION_SCHEMA instead?
//SELECT *
// FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
// WHERE CONSTRAINT_SCHEMA = '$dbname'
// AND TABLE_NAME = '$table'
// AND CONSTRAINT_NAME = '$constraint_name';
$query = 'SHOW CREATE TABLE '. $db->escape($table);
$constraint = $db->queryOne($query, 'text', 1);
if (!MDB2::isError($constraint) && !empty($constraint)) {
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$constraint = strtolower($constraint);
} else {
$constraint = strtoupper($constraint);
}
}
$constraint_name_original = $constraint_name;
$constraint_name = $db->getIndexName($constraint_name);
$pattern = '/\bCONSTRAINT\s+'.$constraint_name.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^\s]+) \(([^\)]+)\)(?: ON DELETE ([^\s]+))?(?: ON UPDATE ([^\s]+))?/i';
if (!preg_match($pattern, str_replace('`', '', $constraint), $matches)) {
//fallback to original constraint name
$pattern = '/\bCONSTRAINT\s+'.$constraint_name_original.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^\s]+) \(([^\)]+)\)(?: ON DELETE ([^\s]+))?(?: ON UPDATE ([^\s]+))?/i';
}
if (preg_match($pattern, str_replace('`', '', $constraint), $matches)) {
$definition['foreign'] = true;
$column_names = explode(',', $matches[1]);
$referenced_cols = explode(',', $matches[3]);
$definition['references'] = array(
'table' => $matches[2],
'fields' => array(),
);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
$colpos = 1;
foreach ($referenced_cols as $column_name) {
$definition['references']['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
$definition['ondelete'] = empty($matches[4]) ? 'RESTRICT' : strtoupper($matches[4]);
$definition['onupdate'] = empty($matches[5]) ? 'RESTRICT' : strtoupper($matches[5]);
$definition['match'] = 'SIMPLE';
return $definition;
}
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'SELECT trigger_name,
event_object_table AS table_name,
action_statement AS trigger_body,
action_timing AS trigger_type,
event_manipulation AS trigger_event
FROM information_schema.triggers
WHERE trigger_name = '. $db->quote($trigger, 'text');
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($def)) {
return $def;
}
$def['trigger_comment'] = '';
$def['trigger_enabled'] = true;
return $def;
}
// }}}
// {{{ tableInfo()
/**
* Returns information about a table or a result set
*
* @param object|string $result MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::setOption()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_object($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @mysqli_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$tmp = @mysqli_fetch_field($resource);
$flags = '';
foreach ($this->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->types[$tmp->type])
? $this->types[$tmp->type] : 'unknown',
// http://bugs.php.net/?id=36579
'length' => $tmp->length,
'flags' => $flags,
);
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (MDB2::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
return $res;
}
}
?>

View File

@ -0,0 +1,621 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 Oracle driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
*/
class MDB2_Driver_Reverse_oci8 extends MDB2_Driver_Reverse_Common
{
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
list($owner, $table) = $this->splitTableSchema($table_name);
if (empty($owner)) {
$owner = $db->dsn['username'];
}
$query = 'SELECT column_name name,
data_type "type",
nullable,
data_default "default",
COALESCE(data_precision, data_length) "length",
data_scale "scale"
FROM all_tab_columns
WHERE (table_name=? OR table_name=?)
AND (owner=? OR owner=?)
AND (column_name=? OR column_name=?)
ORDER BY column_id';
$stmt = $db->prepare($query);
if (MDB2::isError($stmt)) {
return $stmt;
}
$args = array(
$table,
strtoupper($table),
$owner,
strtoupper($owner),
$field_name,
strtoupper($field_name)
);
$result = $stmt->execute($args);
if (MDB2::isError($result)) {
return $result;
}
$column = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($column)) {
return $column;
}
$stmt->free();
$result->free();
if (empty($column)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$field_name . ' is not a column in table ' . $table_name, __FUNCTION__);
}
$column = array_change_key_case($column, CASE_LOWER);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
}
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (!empty($column['nullable']) && $column['nullable'] == 'N') {
$notnull = true;
}
$default = false;
if (array_key_exists('default', $column)) {
$default = $column['default'];
if ($default === 'NULL') {
$default = null;
}
if ((null === $default) && $notnull) {
$default = '';
}
}
$definition[0] = array('notnull' => $notnull, 'nativetype' => $column['type']);
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
if ($type == 'integer') {
$query= "SELECT trigger_body
FROM all_triggers
WHERE table_name=?
AND triggering_event='INSERT'
AND trigger_type='BEFORE EACH ROW'";
// ^^ pretty reasonable mimic for "auto_increment" in oracle?
$stmt = $db->prepare($query);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute(strtoupper($table));
if (MDB2::isError($result)) {
return $result;
}
while ($triggerstr = $result->fetchOne()) {
if (preg_match('/.*SELECT\W+(.+)\.nextval +into +\:NEW\.'.$field_name.' +FROM +dual/im', $triggerstr, $matches)) {
$definition[0]['autoincrement'] = $matches[1];
}
}
$stmt->free();
$result->free();
}
return $definition;
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($owner, $table) = $this->splitTableSchema($table_name);
if (empty($owner)) {
$owner = $db->dsn['username'];
}
$query = "SELECT aic.column_name,
aic.column_position,
aic.descend,
aic.table_owner,
alc.constraint_type
FROM all_ind_columns aic
LEFT JOIN all_constraints alc
ON aic.index_name = alc.constraint_name
AND aic.table_name = alc.table_name
AND aic.table_owner = alc.owner
WHERE (aic.table_name=? OR aic.table_name=?)
AND (aic.index_name=? OR aic.index_name=?)
AND (aic.table_owner=? OR aic.table_owner=?)
ORDER BY column_position";
$stmt = $db->prepare($query);
if (MDB2::isError($stmt)) {
return $stmt;
}
$indexnames = array_unique(array($db->getIndexName($index_name), $index_name));
$i = 0;
$row = null;
while ((null === $row) && array_key_exists($i, $indexnames)) {
$args = array(
$table,
strtoupper($table),
$indexnames[$i],
strtoupper($indexnames[$i]),
$owner,
strtoupper($owner)
);
$result = $stmt->execute($args);
if (MDB2::isError($result)) {
return $result;
}
$row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($row)) {
return $row;
}
$i++;
}
if (null === $row) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$index_name. ' is not an index on table '. $table_name, __FUNCTION__);
}
if ($row['constraint_type'] == 'U' || $row['constraint_type'] == 'P') {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$index_name. ' is a constraint, not an index on table '. $table_name, __FUNCTION__);
}
$definition = array();
while (null !== $row) {
$row = array_change_key_case($row, CASE_LOWER);
$column_name = $row['column_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['column_position'],
);
if (!empty($row['descend'])) {
$definition['fields'][$column_name]['sorting'] =
($row['descend'] == 'ASC' ? 'ascending' : 'descending');
}
$row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$index_name. ' is not an index on table '. $table_name, __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($owner, $table) = $this->splitTableSchema($table_name);
if (empty($owner)) {
$owner = $db->dsn['username'];
}
$query = 'SELECT alc.constraint_name,
CASE alc.constraint_type WHEN \'P\' THEN 1 ELSE 0 END "primary",
CASE alc.constraint_type WHEN \'R\' THEN 1 ELSE 0 END "foreign",
CASE alc.constraint_type WHEN \'U\' THEN 1 ELSE 0 END "unique",
CASE alc.constraint_type WHEN \'C\' THEN 1 ELSE 0 END "check",
alc.DELETE_RULE "ondelete",
\'NO ACTION\' "onupdate",
\'SIMPLE\' "match",
CASE alc.deferrable WHEN \'NOT DEFERRABLE\' THEN 0 ELSE 1 END "deferrable",
CASE alc.deferred WHEN \'IMMEDIATE\' THEN 0 ELSE 1 END "initiallydeferred",
alc.search_condition,
alc.table_name,
cols.column_name,
cols.position,
r_alc.table_name "references_table",
r_cols.column_name "references_field",
r_cols.position "references_field_position"
FROM all_cons_columns cols
LEFT JOIN all_constraints alc
ON alc.constraint_name = cols.constraint_name
AND alc.owner = cols.owner
LEFT JOIN all_constraints r_alc
ON alc.r_constraint_name = r_alc.constraint_name
AND alc.r_owner = r_alc.owner
LEFT JOIN all_cons_columns r_cols
ON r_alc.constraint_name = r_cols.constraint_name
AND r_alc.owner = r_cols.owner
AND cols.position = r_cols.position
WHERE (alc.constraint_name=? OR alc.constraint_name=?)
AND alc.constraint_name = cols.constraint_name
AND (alc.owner=? OR alc.owner=?)';
$tablenames = array();
if (!empty($table)) {
$query.= ' AND (alc.table_name=? OR alc.table_name=?)';
$tablenames = array($table, strtoupper($table));
}
$stmt = $db->prepare($query);
if (MDB2::isError($stmt)) {
return $stmt;
}
$constraintnames = array_unique(array($db->getIndexName($constraint_name), $constraint_name));
$c = 0;
$row = null;
while ((null === $row) && array_key_exists($c, $constraintnames)) {
$args = array(
$constraintnames[$c],
strtoupper($constraintnames[$c]),
$owner,
strtoupper($owner)
);
if (!empty($table)) {
$args = array_merge($args, $tablenames);
}
$result = $stmt->execute($args);
if (MDB2::isError($result)) {
return $result;
}
$row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($row)) {
return $row;
}
$c++;
}
$definition = array(
'primary' => (boolean)$row['primary'],
'unique' => (boolean)$row['unique'],
'foreign' => (boolean)$row['foreign'],
'check' => (boolean)$row['check'],
'deferrable' => (boolean)$row['deferrable'],
'initiallydeferred' => (boolean)$row['initiallydeferred'],
'ondelete' => $row['ondelete'],
'onupdate' => $row['onupdate'],
'match' => $row['match'],
);
if ($definition['check']) {
// pattern match constraint for check constraint values into enum-style output:
$enumregex = '/'.$row['column_name'].' in \((.+?)\)/i';
if (preg_match($enumregex, $row['search_condition'], $rangestr)) {
$definition['fields'][$column_name] = array();
$allowed = explode(',', $rangestr[1]);
foreach ($allowed as $val) {
$val = trim($val);
$val = preg_replace('/^\'/', '', $val);
$val = preg_replace('/\'$/', '', $val);
array_push($definition['fields'][$column_name], $val);
}
}
}
while (null !== $row) {
$row = array_change_key_case($row, CASE_LOWER);
$column_name = $row['column_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['position']
);
if ($row['foreign']) {
$ref_column_name = $row['references_field'];
$ref_table_name = $row['references_table'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$ref_column_name = strtolower($ref_column_name);
$ref_table_name = strtolower($ref_table_name);
} else {
$ref_column_name = strtoupper($ref_column_name);
$ref_table_name = strtoupper($ref_table_name);
}
}
$definition['references']['table'] = $ref_table_name;
$definition['references']['fields'][$ref_column_name] = array(
'position' => (int)$row['references_field_position']
);
}
$lastrow = $row;
$row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not a constraint on table '. $table_name, __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getSequenceDefinition()
/**
* Get the structure of a sequence into an array
*
* @param string $sequence name of sequence that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getSequenceDefinition($sequence)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$sequence_name = $db->getSequenceName($sequence);
$query = 'SELECT last_number FROM user_sequences';
$query.= ' WHERE sequence_name='.$db->quote($sequence_name, 'text');
$query.= ' OR sequence_name='.$db->quote(strtoupper($sequence_name), 'text');
$start = $db->queryOne($query, 'integer');
if (MDB2::isError($start)) {
return $start;
}
$definition = array();
if ($start != 1) {
$definition = array('start' => $start);
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = 'SELECT trigger_name,
table_name,
trigger_body,
trigger_type,
triggering_event trigger_event,
description trigger_comment,
1 trigger_enabled,
when_clause
FROM user_triggers
WHERE trigger_name = \''. strtoupper($trigger).'\'';
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
'when_clause' => 'text',
);
$result = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($result)) {
return $result;
}
if (!empty($result['trigger_type'])) {
//$result['trigger_type'] = array_shift(explode(' ', $result['trigger_type']));
$result['trigger_type'] = preg_replace('/(\S+).*/', '\\1', $result['trigger_type']);
}
return $result;
}
// }}}
// {{{ 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 MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @OCINumCols($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$column = array(
'table' => '',
'name' => $case_func(@OCIColumnName($resource, $i+1)),
'type' => @OCIColumnType($resource, $i+1),
'length' => @OCIColumnSize($resource, $i+1),
'flags' => '',
);
$res[$i] = $column;
$res[$i]['mdb2type'] = $db->datatype->mapNativeDatatype($res[$i]);
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
return $res;
}
}
?>

View File

@ -0,0 +1,631 @@
<?php
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 MSSQL driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_odbc extends MDB2_Driver_Reverse_Common
{
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$fldname = $db->quoteIdentifier($field_name, true);
$res = odbc_columns($db->connection,"%","%",$table);
$column = array();
while($data = odbc_fetch_array($res)) {
if(strcasecmp($field_name,$data['COLUMN_NAME'])) {
$column['table_name'] = $data['TABLE_NAME'];
$column['name'] = $data['COLUMN_NAME'];
$column['type'] = $data['TYPE_NAME'];
if($data['IS_NULLABLE'] == "YES") {
$column['is_nullable'] = 1;
} else {
$column['is_nullable'] = 0;
}
$column['column_default'] = $data['COLUMN_DEF'];
$column['length'] = $data['COLUMNZ_SIZE'];
$column['numeric_precision'] = $data['NUM_PREC_RADIX'];
$column['numeric_scale'] = $data['DECIMAL_DIGITS'];
$column['collation_name'] = NULL;
}
}
/*
$query = "SELECT t.table_name,
c.column_name 'name',
c.data_type 'type',
CASE c.is_nullable WHEN 'YES' THEN 1 ELSE 0 END AS 'is_nullable',
c.column_default,
c.character_maximum_length 'length',
c.numeric_precision,
c.numeric_scale,
c.character_set_name,
c.collation_name
FROM INFORMATION_SCHEMA.TABLES t,
INFORMATION_SCHEMA.COLUMNS c
WHERE t.table_name = c.table_name
AND t.table_name = '$table'
AND c.column_name = '$fldname'";
if (!empty($schema)) {
$query .= " AND t.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ' ORDER BY t.table_name';
$column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($column)) {
return $column;
}
*/
if (empty($column)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = true;
if ($column['is_nullable']) {
$notnull = false;
}
$default = false;
if (array_key_exists('column_default', $column)) {
$default = $column['column_default'];
if (is_null($default) && $notnull) {
$default = '';
} elseif (strlen($default) > 4
&& substr($default, 0, 1) == '('
&& substr($default, -1, 1) == ')'
) {
//mssql wraps the default value in parentheses: "((1234))", "(NULL)"
$default = trim($default, '()');
if ($default == 'NULL') {
$default = null;
}
}
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
if (!is_null($length)) {
$definition[0]['length'] = $length;
}
if (!is_null($unsigned)) {
$definition[0]['unsigned'] = $unsigned;
}
if (!is_null($fixed)) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
//$idxname = $db->quoteIdentifier($index_name, true);
$query = "SELECT OBJECT_NAME(i.id) tablename,
i.name indexname,
c.name field_name,
CASE INDEXKEY_PROPERTY(i.id, i.indid, ik.keyno, 'IsDescending')
WHEN 1 THEN 'DESC' ELSE 'ASC'
END 'collation',
ik.keyno 'position'
FROM sysindexes i
JOIN sysindexkeys ik ON ik.id = i.id AND ik.indid = i.indid
JOIN syscolumns c ON c.id = ik.id AND c.colid = ik.colid
WHERE OBJECT_NAME(i.id) = '$table'
AND i.name = '%s'
AND NOT EXISTS (
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
WHERE k.table_name = OBJECT_NAME(i.id)
AND k.constraint_name = i.name";
if (!empty($schema)) {
$query .= " AND k.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ')
ORDER BY tablename, indexname, ik.keyno';
$index_name_mdb2 = $db->getIndexName($index_name);
$result = $db->queryRow(sprintf($query, $index_name_mdb2));
if (!MDB2::isError($result) && !is_null($result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$index_name = $index_name_mdb2;
}
$result = $db->query(sprintf($query, $index_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array();
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['position'],
);
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'ASC'
? 'ascending' : 'descending');
}
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$query = "SELECT k.table_name,
k.column_name field_name,
CASE c.constraint_type WHEN 'PRIMARY KEY' THEN 1 ELSE 0 END 'primary',
CASE c.constraint_type WHEN 'UNIQUE' THEN 1 ELSE 0 END 'unique',
CASE c.constraint_type WHEN 'FOREIGN KEY' THEN 1 ELSE 0 END 'foreign',
CASE c.constraint_type WHEN 'CHECK' THEN 1 ELSE 0 END 'check',
CASE c.is_deferrable WHEN 'NO' THEN 0 ELSE 1 END 'deferrable',
CASE c.initially_deferred WHEN 'NO' THEN 0 ELSE 1 END 'initiallydeferred',
rc.match_option 'match',
rc.update_rule 'onupdate',
rc.delete_rule 'ondelete',
kcu.table_name 'references_table',
kcu.column_name 'references_field',
k.ordinal_position 'field_position'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS c
ON k.table_name = c.table_name
AND k.table_schema = c.table_schema
AND k.table_catalog = c.table_catalog
AND k.constraint_catalog = c.constraint_catalog
AND k.constraint_name = c.constraint_name
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
ON rc.constraint_schema = c.constraint_schema
AND rc.constraint_catalog = c.constraint_catalog
AND rc.constraint_name = c.constraint_name
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
ON rc.unique_constraint_schema = kcu.constraint_schema
AND rc.unique_constraint_catalog = kcu.constraint_catalog
AND rc.unique_constraint_name = kcu.constraint_name
AND k.ordinal_position = kcu.ordinal_position
WHERE k.constraint_catalog = DB_NAME()
AND k.table_name = '$table'
AND k.constraint_name = '%s'";
if (!empty($schema)) {
$query .= " AND k.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ' ORDER BY k.constraint_name,
k.ordinal_position';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$result = $db->queryRow(sprintf($query, $constraint_name_mdb2));
if (!MDB2::isError($result) && !is_null($result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$constraint_name = $constraint_name_mdb2;
}
$result = $db->query(sprintf($query, $constraint_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array(
'fields' => array()
);
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['field_position']
);
if ($row['foreign']) {
$ref_column_name = $row['references_field'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$ref_column_name = strtolower($ref_column_name);
} else {
$ref_column_name = strtoupper($ref_column_name);
}
}
$definition['references']['table'] = $row['references_table'];
$definition['references']['fields'][$ref_column_name] = array(
'position' => (int)$row['field_position']
);
}
//collation?!?
/*
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'ASC'
? 'ascending' : 'descending');
}
*/
$lastrow = $row;
// otherwise $row is no longer usable on exit from loop
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$definition['primary'] = (boolean)$lastrow['primary'];
$definition['unique'] = (boolean)$lastrow['unique'];
$definition['foreign'] = (boolean)$lastrow['foreign'];
$definition['check'] = (boolean)$lastrow['check'];
$definition['deferrable'] = (boolean)$lastrow['deferrable'];
$definition['initiallydeferred'] = (boolean)$lastrow['initiallydeferred'];
$definition['onupdate'] = $lastrow['onupdate'];
$definition['ondelete'] = $lastrow['ondelete'];
$definition['match'] = $lastrow['match'];
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT sys1.name trigger_name,
sys2.name table_name,
c.text trigger_body,
c.encrypted is_encripted,
CASE
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsTriggerDisabled') = 1
THEN 0 ELSE 1
END trigger_enabled,
CASE
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsInsertTrigger') = 1
THEN 'INSERT'
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsUpdateTrigger') = 1
THEN 'UPDATE'
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsDeleteTrigger') = 1
THEN 'DELETE'
END trigger_event,
CASE WHEN OBJECTPROPERTY(sys1.id, 'ExecIsInsteadOfTrigger') = 1
THEN 'INSTEAD OF' ELSE 'AFTER'
END trigger_type,
'' trigger_comment
FROM sysobjects sys1
JOIN sysobjects sys2 ON sys1.parent_obj = sys2.id
JOIN syscomments c ON sys1.id = c.id
WHERE sys1.xtype = 'TR'
AND sys1.name = ". $db->quote($trigger, 'text');
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
'is_encripted' => 'boolean',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($def)) {
return $def;
}
$trg_body = $db->queryCol('EXEC sp_helptext '. $db->quote($trigger, 'text'), 'text');
if (!MDB2::isError($trg_body)) {
$def['trigger_body'] = implode(' ', $trg_body);
}
return $def;
}
// }}}
// {{{ 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 MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @mssql_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => '',
'name' => $case_func(@mssql_field_name($resource, $i)),
'type' => @mssql_field_type($resource, $i),
'length' => @mssql_field_length($resource, $i),
'flags' => '',
);
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (MDB2::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
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 protected
* @author Joern Barthel <j_barthel@web.de>
*/
function _mssql_field_flags($table, $column)
{
$db =& $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
static $tableName = null;
static $flags = array();
if ($table != $tableName) {
$flags = array();
$tableName = $table;
// get unique and primary keys
$res = $db->queryAll("EXEC SP_HELPINDEX[$table]", null, MDB2_FETCHMODE_ASSOC);
foreach ($res as $val) {
$val = array_change_key_case($val, CASE_LOWER);
$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 = $db->queryAll("EXEC SP_COLUMNS[$table]", null, MDB2_FETCHMODE_ASSOC);
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 (!empty($flags[$column])) {
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 protected
* @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);
}
}
// }}}
}
?>

View File

@ -0,0 +1,574 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Paul Cooper <pgc@ucecom.com> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 PostGreSQL driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_pgsql extends MDB2_Driver_Reverse_Common
{
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT a.attname AS name,
t.typname AS type,
CASE a.attlen
WHEN -1 THEN
CASE t.typname
WHEN 'numeric' THEN (a.atttypmod / 65536)
WHEN 'decimal' THEN (a.atttypmod / 65536)
WHEN 'money' THEN (a.atttypmod / 65536)
ELSE CASE a.atttypmod
WHEN -1 THEN NULL
ELSE a.atttypmod - 4
END
END
ELSE a.attlen
END AS length,
CASE t.typname
WHEN 'numeric' THEN (a.atttypmod % 65536) - 4
WHEN 'decimal' THEN (a.atttypmod % 65536) - 4
WHEN 'money' THEN (a.atttypmod % 65536) - 4
ELSE 0
END AS scale,
a.attnotnull,
a.atttypmod,
a.atthasdef,
(SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_attrdef d
WHERE d.adrelid = a.attrelid
AND d.adnum = a.attnum
AND a.atthasdef
) as default
FROM pg_attribute a,
pg_class c,
pg_type t
WHERE c.relname = ".$db->quote($table, 'text')."
AND a.atttypid = t.oid
AND c.oid = a.attrelid
AND NOT a.attisdropped
AND a.attnum > 0
AND a.attname = ".$db->quote($field_name, 'text')."
ORDER BY a.attnum";
$column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($column)) {
return $column;
}
if (empty($column)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
$column = array_change_key_case($column, CASE_LOWER);
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (!empty($column['attnotnull']) && $column['attnotnull'] == 't') {
$notnull = true;
}
$default = null;
if ($column['atthasdef'] === 't'
&& strpos($column['default'], 'NULL') !== 0
&& !preg_match("/nextval\('([^']+)'/", $column['default'])
) {
$pattern = '/^\'(.*)\'::[\w ]+$/i';
$default = $column['default'];#substr($column['adsrc'], 1, -1);
if ((null === $default) && $notnull) {
$default = '';
} elseif (!empty($default) && preg_match($pattern, $default)) {
//remove data type cast
$default = preg_replace ($pattern, '\\1', $default);
}
}
$autoincrement = false;
if (preg_match("/nextval\('([^']+)'/", $column['default'], $nextvals)) {
$autoincrement = true;
}
$definition[0] = array('notnull' => $notnull, 'nativetype' => $column['type']);
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
if ($autoincrement !== false) {
$definition[0]['autoincrement'] = $autoincrement;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = 'SELECT relname, indkey FROM pg_index, pg_class';
$query.= ' WHERE pg_class.oid = pg_index.indexrelid';
$query.= " AND indisunique != 't' AND indisprimary != 't'";
$query.= ' AND pg_class.relname = %s';
$index_name_mdb2 = $db->getIndexName($index_name);
$row = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($row) || empty($row)) {
// fallback to the given $index_name, without transformation
$row = $db->queryRow(sprintf($query, $db->quote($index_name, 'text')), null, MDB2_FETCHMODE_ASSOC);
}
if (MDB2::isError($row)) {
return $row;
}
if (empty($row)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$row = array_change_key_case($row, CASE_LOWER);
$db->loadModule('Manager', null, true);
$columns = $db->manager->listTableFields($table_name);
$definition = array();
$index_column_numbers = explode(' ', $row['indkey']);
$colpos = 1;
foreach ($index_column_numbers as $number) {
$definition['fields'][$columns[($number - 1)]] = array(
'position' => $colpos++,
'sorting' => 'ascending',
);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT c.oid,
c.conname AS constraint_name,
CASE WHEN c.contype = 'c' THEN 1 ELSE 0 END AS \"check\",
CASE WHEN c.contype = 'f' THEN 1 ELSE 0 END AS \"foreign\",
CASE WHEN c.contype = 'p' THEN 1 ELSE 0 END AS \"primary\",
CASE WHEN c.contype = 'u' THEN 1 ELSE 0 END AS \"unique\",
CASE WHEN c.condeferrable = 'f' THEN 0 ELSE 1 END AS deferrable,
CASE WHEN c.condeferred = 'f' THEN 0 ELSE 1 END AS initiallydeferred,
--array_to_string(c.conkey, ' ') AS constraint_key,
t.relname AS table_name,
t2.relname AS references_table,
CASE confupdtype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
END AS onupdate,
CASE confdeltype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
END AS ondelete,
CASE confmatchtype
WHEN 'u' THEN 'UNSPECIFIED'
WHEN 'f' THEN 'FULL'
WHEN 'p' THEN 'PARTIAL'
END AS match,
--array_to_string(c.confkey, ' ') AS fk_constraint_key,
consrc
FROM pg_constraint c
LEFT JOIN pg_class t ON c.conrelid = t.oid
LEFT JOIN pg_class t2 ON c.confrelid = t2.oid
WHERE c.conname = %s
AND t.relname = " . $db->quote($table, 'text');
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($row) || empty($row)) {
// fallback to the given $index_name, without transformation
$constraint_name_mdb2 = $constraint_name;
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
}
if (MDB2::isError($row)) {
return $row;
}
$uniqueIndex = false;
if (empty($row)) {
// We might be looking for a UNIQUE index that was not created
// as a constraint but should be treated as such.
$query = 'SELECT relname AS constraint_name,
indkey,
0 AS "check",
0 AS "foreign",
0 AS "primary",
1 AS "unique",
0 AS deferrable,
0 AS initiallydeferred,
NULL AS references_table,
NULL AS onupdate,
NULL AS ondelete,
NULL AS match
FROM pg_index, pg_class
WHERE pg_class.oid = pg_index.indexrelid
AND indisunique = \'t\'
AND pg_class.relname = %s';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($row) || empty($row)) {
// fallback to the given $index_name, without transformation
$constraint_name_mdb2 = $constraint_name;
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
}
if (MDB2::isError($row)) {
return $row;
}
if (empty($row)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$uniqueIndex = true;
}
$row = array_change_key_case($row, CASE_LOWER);
$definition = array(
'primary' => (boolean)$row['primary'],
'unique' => (boolean)$row['unique'],
'foreign' => (boolean)$row['foreign'],
'check' => (boolean)$row['check'],
'fields' => array(),
'references' => array(
'table' => $row['references_table'],
'fields' => array(),
),
'deferrable' => (boolean)$row['deferrable'],
'initiallydeferred' => (boolean)$row['initiallydeferred'],
'onupdate' => $row['onupdate'],
'ondelete' => $row['ondelete'],
'match' => $row['match'],
);
if ($uniqueIndex) {
$db->loadModule('Manager', null, true);
$columns = $db->manager->listTableFields($table_name);
$index_column_numbers = explode(' ', $row['indkey']);
$colpos = 1;
foreach ($index_column_numbers as $number) {
$definition['fields'][$columns[($number - 1)]] = array(
'position' => $colpos++,
'sorting' => 'ascending',
);
}
return $definition;
}
$query = 'SELECT a.attname
FROM pg_constraint c
LEFT JOIN pg_class t ON c.conrelid = t.oid
LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.conkey)
WHERE c.conname = %s
AND t.relname = ' . $db->quote($table, 'text');
$fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
if (MDB2::isError($fields)) {
return $fields;
}
$colpos = 1;
foreach ($fields as $field) {
$definition['fields'][$field] = array(
'position' => $colpos++,
'sorting' => 'ascending',
);
}
if ($definition['foreign']) {
$query = 'SELECT a.attname
FROM pg_constraint c
LEFT JOIN pg_class t ON c.confrelid = t.oid
LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.confkey)
WHERE c.conname = %s
AND t.relname = ' . $db->quote($definition['references']['table'], 'text');
$foreign_fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
if (MDB2::isError($foreign_fields)) {
return $foreign_fields;
}
$colpos = 1;
foreach ($foreign_fields as $foreign_field) {
$definition['references']['fields'][$foreign_field] = array(
'position' => $colpos++,
);
}
}
if ($definition['check']) {
$check_def = $db->queryOne("SELECT pg_get_constraintdef(" . $row['oid'] . ", 't')");
// ...
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*
* @TODO: add support for plsql functions and functions with args
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT trg.tgname AS trigger_name,
tbl.relname AS table_name,
CASE
WHEN p.proname IS NOT NULL THEN 'EXECUTE PROCEDURE ' || p.proname || '();'
ELSE ''
END AS trigger_body,
CASE trg.tgtype & cast(2 as int2)
WHEN 0 THEN 'AFTER'
ELSE 'BEFORE'
END AS trigger_type,
CASE trg.tgtype & cast(28 as int2)
WHEN 16 THEN 'UPDATE'
WHEN 8 THEN 'DELETE'
WHEN 4 THEN 'INSERT'
WHEN 20 THEN 'INSERT, UPDATE'
WHEN 28 THEN 'INSERT, UPDATE, DELETE'
WHEN 24 THEN 'UPDATE, DELETE'
WHEN 12 THEN 'INSERT, DELETE'
END AS trigger_event,
CASE trg.tgenabled
WHEN 'O' THEN 't'
ELSE trg.tgenabled
END AS trigger_enabled,
obj_description(trg.oid, 'pg_trigger') AS trigger_comment
FROM pg_trigger trg,
pg_class tbl,
pg_proc p
WHERE trg.tgrelid = tbl.oid
AND trg.tgfoid = p.oid
AND trg.tgname = ". $db->quote($trigger, 'text');
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
);
return $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
}
// }}}
// {{{ 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 MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @pg_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => function_exists('pg_field_table') ? @pg_field_table($resource, $i) : '',
'name' => $case_func(@pg_field_name($resource, $i)),
'type' => @pg_field_type($resource, $i),
'length' => @pg_field_size($resource, $i),
'flags' => '',
);
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (MDB2::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
return $res;
}
}
?>

View File

@ -0,0 +1,611 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 SQlite driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Reverse_sqlite extends MDB2_Driver_Reverse_Common
{
/**
* Remove SQL comments from the field definition
*
* @access private
*/
function _removeComments($sql) {
$lines = explode("\n", $sql);
foreach ($lines as $k => $line) {
$pieces = explode('--', $line);
if (count($pieces) > 1 && (substr_count($pieces[0], '\'') % 2) == 0) {
$lines[$k] = substr($line, 0, strpos($line, '--'));
}
}
return implode("\n", $lines);
}
/**
*
*/
function _getTableColumns($sql)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_def = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
// replace the decimal length-places-separator with a colon
$column_def = preg_replace('/(\d),(\d)/', '\1:\2', $column_def);
$column_def = $this->_removeComments($column_def);
$column_sql = explode(',', $column_def);
$columns = array();
$count = count($column_sql);
if ($count == 0) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unexpected empty table column definition list', __FUNCTION__);
}
$regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|TINYINT|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i';
$regexp2 = '/^\s*([^ ]+) +(PRIMARY|UNIQUE|CHECK)$/i';
for ($i=0, $j=0; $i<$count; ++$i) {
if (!preg_match($regexp, trim($column_sql[$i]), $matches)) {
if (!preg_match($regexp2, trim($column_sql[$i]))) {
continue;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unexpected table column SQL definition: "'.$column_sql[$i].'"', __FUNCTION__);
}
$columns[$j]['name'] = trim($matches[1], implode('', $db->identifier_quoting));
$columns[$j]['type'] = strtolower($matches[2]);
if (isset($matches[4]) && strlen($matches[4])) {
$columns[$j]['length'] = $matches[4];
}
if (isset($matches[6]) && strlen($matches[6])) {
$columns[$j]['decimal'] = $matches[6];
}
if (isset($matches[8]) && strlen($matches[8])) {
$columns[$j]['unsigned'] = true;
}
if (isset($matches[9]) && strlen($matches[9])) {
$columns[$j]['autoincrement'] = true;
}
if (isset($matches[12]) && strlen($matches[12])) {
$default = $matches[12];
if (strlen($default) && $default[0]=="'") {
$default = str_replace("''", "'", substr($default, 1, strlen($default)-2));
}
if ($default === 'NULL') {
$default = null;
}
$columns[$j]['default'] = $default;
} else {
$columns[$j]['default'] = null;
}
if (isset($matches[7]) && strlen($matches[7])) {
$columns[$j]['notnull'] = ($matches[7] === ' NOT NULL');
} else if (isset($matches[9]) && strlen($matches[9])) {
$columns[$j]['notnull'] = ($matches[9] === ' NOT NULL');
} else if (isset($matches[13]) && strlen($matches[13])) {
$columns[$j]['notnull'] = ($matches[13] === ' NOT NULL');
}
++$j;
}
return $columns;
}
// {{{ getTableFieldDefinition()
/**
* Get the stucture of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure.
* The returned array contains an array for each field definition,
* with (some of) these indices:
* [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type]
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= 'name='.$db->quote($table, 'text');
}
$sql = $db->queryOne($query);
if (MDB2::isError($sql)) {
return $sql;
}
$columns = $this->_getTableColumns($sql);
foreach ($columns as $column) {
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
if ($field_name == $column['name']) {
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (!empty($column['notnull'])) {
$notnull = $column['notnull'];
}
$default = false;
if (array_key_exists('default', $column)) {
$default = $column['default'];
if ((null === $default) && $notnull) {
$default = '';
}
}
$autoincrement = false;
if (!empty($column['autoincrement'])) {
$autoincrement = true;
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
if ($autoincrement !== false) {
$definition[0]['autoincrement'] = $autoincrement;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the stucture of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
} else {
$query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
}
$query.= ' AND sql NOT NULL ORDER BY name';
$index_name_mdb2 = $db->getIndexName($index_name);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($index_name_mdb2), 'text'));
} else {
$qry = sprintf($query, $db->quote($index_name_mdb2, 'text'));
}
$sql = $db->queryOne($qry, 'text');
if (MDB2::isError($sql) || empty($sql)) {
// fallback to the given $index_name, without transformation
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($index_name), 'text'));
} else {
$qry = sprintf($query, $db->quote($index_name, 'text'));
}
$sql = $db->queryOne($qry, 'text');
}
if (MDB2::isError($sql)) {
return $sql;
}
if (!$sql) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$sql = strtolower($sql);
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
$column_names = explode(',', $column_names);
if (preg_match("/^create unique/", $sql)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$definition = array();
$count = count($column_names);
for ($i=0; $i<$count; ++$i) {
$column_name = strtok($column_names[$i], ' ');
$collation = strtok(' ');
$definition['fields'][$column_name] = array(
'position' => $i+1
);
if (!empty($collation)) {
$definition['fields'][$column_name]['sorting'] =
($collation=='ASC' ? 'ascending' : 'descending');
}
}
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the stucture of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
} else {
$query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
}
$query.= ' AND sql NOT NULL ORDER BY name';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($constraint_name_mdb2), 'text'));
} else {
$qry = sprintf($query, $db->quote($constraint_name_mdb2, 'text'));
}
$sql = $db->queryOne($qry, 'text');
if (MDB2::isError($sql) || empty($sql)) {
// fallback to the given $index_name, without transformation
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($constraint_name), 'text'));
} else {
$qry = sprintf($query, $db->quote($constraint_name, 'text'));
}
$sql = $db->queryOne($qry, 'text');
}
if (MDB2::isError($sql)) {
return $sql;
}
//default values, eventually overridden
$definition = array(
'primary' => false,
'unique' => false,
'foreign' => false,
'check' => false,
'fields' => array(),
'references' => array(
'table' => '',
'fields' => array(),
),
'onupdate' => '',
'ondelete' => '',
'match' => '',
'deferrable' => false,
'initiallydeferred' => false,
);
if (!$sql) {
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= 'name='.$db->quote($table, 'text');
}
$query.= " AND sql NOT NULL ORDER BY name";
$sql = $db->queryOne($query, 'text');
if (MDB2::isError($sql)) {
return $sql;
}
if ($constraint_name == 'primary') {
// search in table definition for PRIMARY KEYs
if (preg_match("/\bPRIMARY\s+KEY\b\s*\(([^)]+)/i", $sql, $tmp)) {
$definition['primary'] = true;
$definition['fields'] = array();
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
return $definition;
}
if (preg_match("/\"([^\"]+)\"[^\,\"]+\bPRIMARY\s+KEY\b[^\,\)]*/i", $sql, $tmp)) {
$definition['primary'] = true;
$definition['fields'] = array();
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
return $definition;
}
} else {
// search in table definition for FOREIGN KEYs
$pattern = "/\bCONSTRAINT\b\s+%s\s+
\bFOREIGN\s+KEY\b\s*\(([^\)]+)\)\s*
\bREFERENCES\b\s+([^\s]+)\s*\(([^\)]+)\)\s*
(?:\bMATCH\s*([^\s]+))?\s*
(?:\bON\s+UPDATE\s+([^\s,\)]+))?\s*
(?:\bON\s+DELETE\s+([^\s,\)]+))?\s*
/imsx";
$found_fk = false;
if (preg_match(sprintf($pattern, $constraint_name_mdb2), $sql, $tmp)) {
$found_fk = true;
} elseif (preg_match(sprintf($pattern, $constraint_name), $sql, $tmp)) {
$found_fk = true;
}
if ($found_fk) {
$definition['foreign'] = true;
$definition['match'] = 'SIMPLE';
$definition['onupdate'] = 'NO ACTION';
$definition['ondelete'] = 'NO ACTION';
$definition['references']['table'] = $tmp[2];
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
$referenced_cols = explode(',', $tmp[3]);
$colpos = 1;
foreach ($referenced_cols as $column_name) {
$definition['references']['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
if (isset($tmp[4])) {
$definition['match'] = $tmp[4];
}
if (isset($tmp[5])) {
$definition['onupdate'] = $tmp[5];
}
if (isset($tmp[6])) {
$definition['ondelete'] = $tmp[6];
}
return $definition;
}
}
$sql = false;
}
if (!$sql) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$sql = strtolower($sql);
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
$column_names = explode(',', $column_names);
if (!preg_match("/^create unique/", $sql)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$definition['unique'] = true;
$count = count($column_names);
for ($i=0; $i<$count; ++$i) {
$column_name = strtok($column_names[$i]," ");
$collation = strtok(" ");
$definition['fields'][$column_name] = array(
'position' => $i+1
);
if (!empty($collation)) {
$definition['fields'][$column_name]['sorting'] =
($collation=='ASC' ? 'ascending' : 'descending');
}
}
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT name as trigger_name,
tbl_name AS table_name,
sql AS trigger_body,
NULL AS trigger_type,
NULL AS trigger_event,
NULL AS trigger_comment,
1 AS trigger_enabled
FROM sqlite_master
WHERE type='trigger'";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= ' AND LOWER(name)='.$db->quote(strtolower($trigger), 'text');
} else {
$query.= ' AND name='.$db->quote($trigger, 'text');
}
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($def)) {
return $def;
}
if (empty($def)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing trigger', __FUNCTION__);
}
if (preg_match("/^create\s+(?:temp|temporary)?trigger\s+(?:if\s+not\s+exists\s+)?.*(before|after)?\s+(insert|update|delete)/Uims", $def['trigger_body'], $tmp)) {
$def['trigger_type'] = strtoupper($tmp[1]);
$def['trigger_event'] = strtoupper($tmp[2]);
}
return $def;
}
// }}}
// {{{ 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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
* @since Method available since Release 1.7.0
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null,
'This DBMS can not obtain tableInfo from result sets', __FUNCTION__);
}
}
?>

View File

@ -0,0 +1,611 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 - 7 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id$
//
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 SQlite driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_sqlite3 extends MDB2_Driver_Reverse_Common
{
/**
* Remove SQL comments from the field definition
*
* @access private
*/
function _removeComments($sql) {
$lines = explode("\n", $sql);
foreach ($lines as $k => $line) {
$pieces = explode('--', $line);
if (count($pieces) > 1 && (substr_count($pieces[0], '\'') % 2) == 0) {
$lines[$k] = substr($line, 0, strpos($line, '--'));
}
}
return implode("\n", $lines);
}
/**
*
*/
function _getTableColumns($sql)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_def = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
// replace the decimal length-places-separator with a colon
$column_def = preg_replace('/(\d),(\d)/', '\1:\2', $column_def);
$column_def = $this->_removeComments($column_def);
$column_sql = explode(',', $column_def);
$columns = array();
$count = count($column_sql);
if ($count == 0) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unexpected empty table column definition list', __FUNCTION__);
}
$regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|TINYINT|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i';
$regexp2 = '/^\s*([^ ]+) +(PRIMARY|UNIQUE|CHECK)$/i';
for ($i=0, $j=0; $i<$count; ++$i) {
if (!preg_match($regexp, trim($column_sql[$i]), $matches)) {
if (!preg_match($regexp2, trim($column_sql[$i]))) {
continue;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unexpected table column SQL definition: "'.$column_sql[$i].'"', __FUNCTION__);
}
$columns[$j]['name'] = trim($matches[1], implode('', $db->identifier_quoting));
$columns[$j]['type'] = strtolower($matches[2]);
if (isset($matches[4]) && strlen($matches[4])) {
$columns[$j]['length'] = $matches[4];
}
if (isset($matches[6]) && strlen($matches[6])) {
$columns[$j]['decimal'] = $matches[6];
}
if (isset($matches[8]) && strlen($matches[8])) {
$columns[$j]['unsigned'] = true;
}
if (isset($matches[9]) && strlen($matches[9])) {
$columns[$j]['autoincrement'] = true;
}
if (isset($matches[12]) && strlen($matches[12])) {
$default = $matches[12];
if (strlen($default) && $default[0]=="'") {
$default = str_replace("''", "'", substr($default, 1, strlen($default)-2));
}
if ($default === 'NULL') {
$default = null;
}
$columns[$j]['default'] = $default;
} else {
$columns[$j]['default'] = null;
}
if (isset($matches[7]) && strlen($matches[7])) {
$columns[$j]['notnull'] = ($matches[7] === ' NOT NULL');
} else if (isset($matches[9]) && strlen($matches[9])) {
$columns[$j]['notnull'] = ($matches[9] === ' NOT NULL');
} else if (isset($matches[13]) && strlen($matches[13])) {
$columns[$j]['notnull'] = ($matches[13] === ' NOT NULL');
}
++$j;
}
return $columns;
}
// {{{ getTableFieldDefinition()
/**
* Get the stucture of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure.
* The returned array contains an array for each field definition,
* with (some of) these indices:
* [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type]
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= 'name='.$db->quote($table, 'text');
}
$sql = $db->queryOne($query);
if (MDB2::isError($sql)) {
return $sql;
}
$columns = $this->_getTableColumns($sql);
foreach ($columns as $column) {
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
if ($field_name == $column['name']) {
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (!empty($column['notnull'])) {
$notnull = $column['notnull'];
}
$default = false;
if (array_key_exists('default', $column)) {
$default = $column['default'];
if ((null === $default) && $notnull) {
$default = '';
}
}
$autoincrement = false;
if (!empty($column['autoincrement'])) {
$autoincrement = true;
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
if ($autoincrement !== false) {
$definition[0]['autoincrement'] = $autoincrement;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the stucture of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
} else {
$query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
}
$query.= ' AND sql NOT NULL ORDER BY name';
$index_name_mdb2 = $db->getIndexName($index_name);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($index_name_mdb2), 'text'));
} else {
$qry = sprintf($query, $db->quote($index_name_mdb2, 'text'));
}
$sql = $db->queryOne($qry, 'text');
if (MDB2::isError($sql) || empty($sql)) {
// fallback to the given $index_name, without transformation
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($index_name), 'text'));
} else {
$qry = sprintf($query, $db->quote($index_name, 'text'));
}
$sql = $db->queryOne($qry, 'text');
}
if (MDB2::isError($sql)) {
return $sql;
}
if (!$sql) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$sql = strtolower($sql);
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
$column_names = explode(',', $column_names);
if (preg_match("/^create unique/", $sql)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$definition = array();
$count = count($column_names);
for ($i=0; $i<$count; ++$i) {
$column_name = strtok($column_names[$i], ' ');
$collation = strtok(' ');
$definition['fields'][$column_name] = array(
'position' => $i+1
);
if (!empty($collation)) {
$definition['fields'][$column_name]['sorting'] =
($collation=='ASC' ? 'ascending' : 'descending');
}
}
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the stucture of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
} else {
$query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
}
$query.= ' AND sql NOT NULL ORDER BY name';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($constraint_name_mdb2), 'text'));
} else {
$qry = sprintf($query, $db->quote($constraint_name_mdb2, 'text'));
}
$sql = $db->queryOne($qry, 'text');
if (MDB2::isError($sql) || empty($sql)) {
// fallback to the given $index_name, without transformation
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($constraint_name), 'text'));
} else {
$qry = sprintf($query, $db->quote($constraint_name, 'text'));
}
$sql = $db->queryOne($qry, 'text');
}
if (MDB2::isError($sql)) {
return $sql;
}
//default values, eventually overridden
$definition = array(
'primary' => false,
'unique' => false,
'foreign' => false,
'check' => false,
'fields' => array(),
'references' => array(
'table' => '',
'fields' => array(),
),
'onupdate' => '',
'ondelete' => '',
'match' => '',
'deferrable' => false,
'initiallydeferred' => false,
);
if (!$sql) {
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= 'name='.$db->quote($table, 'text');
}
$query.= " AND sql NOT NULL ORDER BY name";
$sql = $db->queryOne($query, 'text');
if (MDB2::isError($sql)) {
return $sql;
}
if ($constraint_name == 'primary') {
// search in table definition for PRIMARY KEYs
if (preg_match("/\bPRIMARY\s+KEY\b\s*\(([^)]+)/i", $sql, $tmp)) {
$definition['primary'] = true;
$definition['fields'] = array();
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
return $definition;
}
if (preg_match("/\"([^\"]+)\"[^\,\"]+\bPRIMARY\s+KEY\b[^\,\)]*/i", $sql, $tmp)) {
$definition['primary'] = true;
$definition['fields'] = array();
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
return $definition;
}
} else {
// search in table definition for FOREIGN KEYs
$pattern = "/\bCONSTRAINT\b\s+%s\s+
\bFOREIGN\s+KEY\b\s*\(([^\)]+)\)\s*
\bREFERENCES\b\s+([^\s]+)\s*\(([^\)]+)\)\s*
(?:\bMATCH\s*([^\s]+))?\s*
(?:\bON\s+UPDATE\s+([^\s,\)]+))?\s*
(?:\bON\s+DELETE\s+([^\s,\)]+))?\s*
/imsx";
$found_fk = false;
if (preg_match(sprintf($pattern, $constraint_name_mdb2), $sql, $tmp)) {
$found_fk = true;
} elseif (preg_match(sprintf($pattern, $constraint_name), $sql, $tmp)) {
$found_fk = true;
}
if ($found_fk) {
$definition['foreign'] = true;
$definition['match'] = 'SIMPLE';
$definition['onupdate'] = 'NO ACTION';
$definition['ondelete'] = 'NO ACTION';
$definition['references']['table'] = $tmp[2];
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
$referenced_cols = explode(',', $tmp[3]);
$colpos = 1;
foreach ($referenced_cols as $column_name) {
$definition['references']['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
if (isset($tmp[4])) {
$definition['match'] = $tmp[4];
}
if (isset($tmp[5])) {
$definition['onupdate'] = $tmp[5];
}
if (isset($tmp[6])) {
$definition['ondelete'] = $tmp[6];
}
return $definition;
}
}
$sql = false;
}
if (!$sql) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$sql = strtolower($sql);
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
$column_names = explode(',', $column_names);
if (!preg_match("/^create unique/", $sql)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$definition['unique'] = true;
$count = count($column_names);
for ($i=0; $i<$count; ++$i) {
$column_name = strtok($column_names[$i]," ");
$collation = strtok(" ");
$definition['fields'][$column_name] = array(
'position' => $i+1
);
if (!empty($collation)) {
$definition['fields'][$column_name]['sorting'] =
($collation=='ASC' ? 'ascending' : 'descending');
}
}
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT name as trigger_name,
tbl_name AS table_name,
sql AS trigger_body,
NULL AS trigger_type,
NULL AS trigger_event,
NULL AS trigger_comment,
1 AS trigger_enabled
FROM sqlite_master
WHERE type='trigger'";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= ' AND LOWER(name)='.$db->quote(strtolower($trigger), 'text');
} else {
$query.= ' AND name='.$db->quote($trigger, 'text');
}
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($def)) {
return $def;
}
if (empty($def)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing trigger', __FUNCTION__);
}
if (preg_match("/^create\s+(?:temp|temporary)?trigger\s+(?:if\s+not\s+exists\s+)?.*(before|after)?\s+(insert|update|delete)/Uims", $def['trigger_body'], $tmp)) {
$def['trigger_type'] = strtoupper($tmp[1]);
$def['trigger_event'] = strtoupper($tmp[2]);
}
return $def;
}
// }}}
// {{{ 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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
* @since Method available since Release 1.7.0
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null,
'This DBMS can not obtain tableInfo from result sets', __FUNCTION__);
}
}
?>

View File

@ -0,0 +1,653 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
require_once 'MDB2/Driver/Reverse/Common.php';
/**
* MDB2 MSSQL driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@dybnet.de>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_sqlsrv extends MDB2_Driver_Reverse_Common
{
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (MDB2::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$fldname = $db->quoteIdentifier($field_name, true);
$query = "SELECT t.table_name,
c.column_name 'name',
c.data_type 'type',
CASE c.is_nullable WHEN 'YES' THEN 1 ELSE 0 END AS 'is_nullable',
c.column_default,
c.character_maximum_length 'length',
c.numeric_precision,
c.numeric_scale,
c.character_set_name,
c.collation_name
FROM INFORMATION_SCHEMA.TABLES t,
INFORMATION_SCHEMA.COLUMNS c
WHERE t.table_name = c.table_name
AND t.table_name = '$table'
AND c.column_name = '$fldname'";
if (!empty($schema)) {
$query .= " AND t.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ' ORDER BY t.table_name';
$column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($column)) {
return $column;
}
if (empty($column)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (MDB2::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = true;
if ($column['is_nullable']) {
$notnull = false;
}
$default = false;
if (array_key_exists('column_default', $column)) {
$default = $column['column_default'];
if ((null === $default) && $notnull) {
$default = '';
} elseif (strlen($default) > 4
&& substr($default, 0, 1) == '('
&& substr($default, -1, 1) == ')'
) {
//mssql wraps the default value in parentheses: "((1234))", "(NULL)"
$default = trim($default, '()');
if ($default == 'NULL') {
$default = null;
}
}
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
if (null !== $length) {
$definition[0]['length'] = $length;
}
if (null !== $unsigned) {
$definition[0]['unsigned'] = $unsigned;
}
if (null !== $fixed) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
//$idxname = $db->quoteIdentifier($index_name, true);
$query = "SELECT OBJECT_NAME(i.id) tablename,
i.name indexname,
c.name field_name,
CASE INDEXKEY_PROPERTY(i.id, i.indid, ik.keyno, 'IsDescending')
WHEN 1 THEN 'DESC' ELSE 'ASC'
END 'collation',
ik.keyno 'position'
FROM sysindexes i
JOIN sysindexkeys ik ON ik.id = i.id AND ik.indid = i.indid
JOIN syscolumns c ON c.id = ik.id AND c.colid = ik.colid
WHERE OBJECT_NAME(i.id) = '$table'
AND i.name = '%s'
AND NOT EXISTS (
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
WHERE k.table_name = OBJECT_NAME(i.id)
AND k.constraint_name = i.name";
if (!empty($schema)) {
$query .= " AND k.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ')
ORDER BY tablename, indexname, ik.keyno';
$index_name_mdb2 = $db->getIndexName($index_name);
$result = $db->queryRow(sprintf($query, $index_name_mdb2));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$index_name = $index_name_mdb2;
}
$result = $db->query(sprintf($query, $index_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array();
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['position'],
);
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'ASC'
? 'ascending' : 'descending');
}
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$query = "SELECT k.table_name,
k.column_name field_name,
CASE c.constraint_type WHEN 'PRIMARY KEY' THEN 1 ELSE 0 END 'primary',
CASE c.constraint_type WHEN 'UNIQUE' THEN 1 ELSE 0 END 'unique',
CASE c.constraint_type WHEN 'FOREIGN KEY' THEN 1 ELSE 0 END 'foreign',
CASE c.constraint_type WHEN 'CHECK' THEN 1 ELSE 0 END 'check',
CASE c.is_deferrable WHEN 'NO' THEN 0 ELSE 1 END 'deferrable',
CASE c.initially_deferred WHEN 'NO' THEN 0 ELSE 1 END 'initiallydeferred',
rc.match_option 'match',
rc.update_rule 'onupdate',
rc.delete_rule 'ondelete',
kcu.table_name 'references_table',
kcu.column_name 'references_field',
k.ordinal_position 'field_position'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS c
ON k.table_name = c.table_name
AND k.table_schema = c.table_schema
AND k.table_catalog = c.table_catalog
AND k.constraint_catalog = c.constraint_catalog
AND k.constraint_name = c.constraint_name
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
ON rc.constraint_schema = c.constraint_schema
AND rc.constraint_catalog = c.constraint_catalog
AND rc.constraint_name = c.constraint_name
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
ON rc.unique_constraint_schema = kcu.constraint_schema
AND rc.unique_constraint_catalog = kcu.constraint_catalog
AND rc.unique_constraint_name = kcu.constraint_name
AND k.ordinal_position = kcu.ordinal_position
WHERE k.constraint_catalog = DB_NAME()
AND k.table_name = '$table'
AND k.constraint_name = '%s'";
if (!empty($schema)) {
$query .= " AND k.table_schema = '" .$db->quoteIdentifier($schema, true) ."'";
}
$query .= ' ORDER BY k.constraint_name,
k.ordinal_position';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$result = $db->queryRow(sprintf($query, $constraint_name_mdb2));
if (!MDB2::isError($result) && (null !== $result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$constraint_name = $constraint_name_mdb2;
}
$result = $db->query(sprintf($query, $constraint_name));
if (MDB2::isError($result)) {
return $result;
}
$definition = array(
'fields' => array()
);
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$column_name = $row['field_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => (int)$row['field_position']
);
if ($row['foreign']) {
$ref_column_name = $row['references_field'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$ref_column_name = strtolower($ref_column_name);
} else {
$ref_column_name = strtoupper($ref_column_name);
}
}
$definition['references']['table'] = $row['references_table'];
$definition['references']['fields'][$ref_column_name] = array(
'position' => (int)$row['field_position']
);
}
//collation?!?
/*
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'ASC'
? 'ascending' : 'descending');
}
*/
$lastrow = $row;
// otherwise $row is no longer usable on exit from loop
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$definition['primary'] = (boolean)$lastrow['primary'];
$definition['unique'] = (boolean)$lastrow['unique'];
$definition['foreign'] = (boolean)$lastrow['foreign'];
$definition['check'] = (boolean)$lastrow['check'];
$definition['deferrable'] = (boolean)$lastrow['deferrable'];
$definition['initiallydeferred'] = (boolean)$lastrow['initiallydeferred'];
$definition['onupdate'] = $lastrow['onupdate'];
$definition['ondelete'] = $lastrow['ondelete'];
$definition['match'] = $lastrow['match'];
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$query = "SELECT sys1.name trigger_name,
sys2.name table_name,
c.text trigger_body,
c.encrypted is_encripted,
CASE
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsTriggerDisabled') = 1
THEN 0 ELSE 1
END trigger_enabled,
CASE
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsInsertTrigger') = 1
THEN 'INSERT'
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsUpdateTrigger') = 1
THEN 'UPDATE'
WHEN OBJECTPROPERTY(sys1.id, 'ExecIsDeleteTrigger') = 1
THEN 'DELETE'
END trigger_event,
CASE WHEN OBJECTPROPERTY(sys1.id, 'ExecIsInsteadOfTrigger') = 1
THEN 'INSTEAD OF' ELSE 'AFTER'
END trigger_type,
'' trigger_comment
FROM sysobjects sys1
JOIN sysobjects sys2 ON sys1.parent_obj = sys2.id
JOIN syscomments c ON sys1.id = c.id
WHERE sys1.xtype = 'TR'
AND sys1.name = ". $db->quote($trigger, 'text');
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
'is_encripted' => 'boolean',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (MDB2::isError($def)) {
return $def;
}
$trg_body = $db->queryCol('EXEC sp_helptext '. $db->quote($trigger, 'text'), 'text');
if (!MDB2::isError($trg_body)) {
$def['trigger_body'] = implode(' ', $trg_body);
}
return $def;
}
// }}}
// {{{ 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 MDB2_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 MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$meta = @sqlsrv_field_metadata($resource);
$count = count($meta);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => '',
'name' => $case_func($meta[$i]['Name']),
'type' => $meta[$i]['Type'],
'length' => $meta[$i]['Size'],
'numeric_precision' => $meta[$i]['Precision'],
'numeric_scale' => $meta[$i]['Scale'],
'flags' => ''
);
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (MDB2::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
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 protected
* @author Joern Barthel <j_barthel@web.de>
*/
function _mssql_field_flags($table, $column)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
static $tableName = null;
static $flags = array();
if ($table != $tableName) {
$flags = array();
$tableName = $table;
// get unique and primary keys
$res = $db->queryAll("EXEC SP_HELPINDEX[$table]", null, MDB2_FETCHMODE_ASSOC);
foreach ($res as $val) {
$val = array_change_key_case($val, CASE_LOWER);
$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 = $db->queryAll("EXEC SP_COLUMNS[$table]", null, MDB2_FETCHMODE_ASSOC);
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 (!empty($flags[$column])) {
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 protected
* @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);
}
}
// }}}
}
?>

View File

@ -0,0 +1,914 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
* MDB2 FrontBase driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
* @author Frank M. Kromann <frank@kromann.info>
*/
class MDB2_Driver_fbsql extends MDB2_Driver_Common
{
// {{{ properties
var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false);
var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
// }}}
// {{{ constructor
/**
* Constructor
*/
function __construct()
{
parent::__construct();
$this->phptype = 'fbsql';
$this->dbsyntax = 'fbsql';
$this->supported['sequences'] = 'emulated';
$this->supported['indexes'] = true;
$this->supported['affected_rows'] = true;
$this->supported['transactions'] = true;
$this->supported['savepoints'] = false;
$this->supported['summary_functions'] = true;
$this->supported['order_by_text'] = true;
$this->supported['current_id'] = 'emulated';
$this->supported['limit_queries'] = 'emulated';
$this->supported['LOBs'] = true;
$this->supported['replace'] ='emulated';
$this->supported['sub_selects'] = true;
$this->supported['auto_increment'] = false; // not implemented
$this->supported['primary_key'] = true;
$this->supported['result_introspection'] = true;
$this->supported['prepared_statements'] = 'emulated';
$this->supported['identifier_quoting'] = true;
$this->supported['pattern_escaping'] = false;
$this->supported['new_link'] = false;
$this->options['DBA_username'] = false;
$this->options['DBA_password'] = false;
}
// }}}
// {{{ errorInfo()
/**
* This method is used to collect information about an error
*
* @param integer $error
* @return array
* @access public
*/
function errorInfo($error = null)
{
if ($this->connection) {
$native_code = @fbsql_errno($this->connection);
$native_msg = @fbsql_error($this->connection);
} else {
$native_code = @fbsql_errno();
$native_msg = @fbsql_error();
}
if (null === $error) {
static $ecode_map;
if (empty($ecode_map)) {
$ecode_map = array(
22 => MDB2_ERROR_SYNTAX,
85 => MDB2_ERROR_ALREADY_EXISTS,
108 => MDB2_ERROR_SYNTAX,
116 => MDB2_ERROR_NOSUCHTABLE,
124 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
215 => MDB2_ERROR_NOSUCHFIELD,
217 => MDB2_ERROR_INVALID_NUMBER,
226 => MDB2_ERROR_NOSUCHFIELD,
231 => MDB2_ERROR_INVALID,
239 => MDB2_ERROR_TRUNCATED,
251 => MDB2_ERROR_SYNTAX,
266 => MDB2_ERROR_NOT_FOUND,
357 => MDB2_ERROR_CONSTRAINT_NOT_NULL,
358 => MDB2_ERROR_CONSTRAINT,
360 => MDB2_ERROR_CONSTRAINT,
361 => MDB2_ERROR_CONSTRAINT,
);
}
if (isset($ecode_map[$native_code])) {
$error = $ecode_map[$native_code];
}
}
return array($error, $native_code, $native_msg);
}
// }}}
// {{{ beginTransaction()
/**
* Start a transaction or set a savepoint.
*
* @param string name of a savepoint to set
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function beginTransaction($savepoint = null)
{
$this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (null !== $savepoint) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
if ($this->in_transaction) {
return MDB2_OK; //nothing to do
}
if (!$this->destructor_registered && $this->opened_persistent) {
$this->destructor_registered = true;
register_shutdown_function('MDB2_closeOpenTransactions');
}
$result =& $this->_doQuery('SET COMMIT FALSE;', true);
if (MDB2::isError($result)) {
return $result;
}
$this->in_transaction = true;
return MDB2_OK;
}
// }}}
// {{{ commit()
/**
* Commit the database changes done during a transaction that is in
* progress or release a savepoint. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after committing the pending changes.
*
* @param string name of a savepoint to release
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function commit($savepoint = null)
{
$this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
}
if (null !== $savepoint) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
$result =& $this->_doQuery('COMMIT;', true);
if (MDB2::isError($result)) {
return $result;
}
$result =& $this->_doQuery('SET COMMIT TRUE;', true);
if (MDB2::isError($result)) {
return $result;
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{
/**
* Cancel any database changes done during a transaction or since a specific
* savepoint that is in progress. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after canceling the pending changes.
*
* @param string name of a savepoint to rollback to
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function rollback($savepoint = null)
{
$this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'rollback cannot be done changes are auto committed', __FUNCTION__);
}
if (null !== $savepoint) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
$result =& $this->_doQuery('ROLLBACK;', true);
if (MDB2::isError($result)) {
return $result;
}
$result =& $this->_doQuery('SET COMMIT TRUE;', true);
if (MDB2::isError($result)) {
return $result;
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{ _doConnect()
/**
* do the grunt work of the connect
*
* @return connection on success or MDB2 Error Object on failure
* @access protected
*/
function _doConnect($username, $password, $persistent = false)
{
if (!extension_loaded($this->phptype)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
}
$params = array(
$this->dsn['hostspec'] ? $this->dsn['hostspec'] : 'localhost',
$username ? $username : null,
$password ? $password : null,
);
$connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
$connection = @call_user_func_array($connect_function, $params);
if ($connection <= 0) {
return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
'unable to establish a connection', __FUNCTION__);
}
if (!empty($this->dsn['charset'])) {
$result = $this->setCharset($this->dsn['charset'], $connection);
if (MDB2::isError($result)) {
return $result;
}
}
return $connection;
}
// }}}
// {{{ connect()
/**
* Connect to the database
*
* @return true on success, MDB2 Error Object on failure
**/
function connect()
{
if (is_resource($this->connection)) {
//if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
if (MDB2::areEquals($this->connected_dsn, $this->dsn)
&& $this->opened_persistent == $this->options['persistent']
) {
return MDB2_OK;
}
$this->disconnect(false);
}
$connection = $this->_doConnect($this->dsn['username'],
$this->dsn['password'],
$this->options['persistent']);
if (MDB2::isError($connection)) {
return $connection;
}
$this->connection = $connection;
$this->connected_dsn = $this->dsn;
$this->connected_database_name = '';
$this->opened_persistent = $this->options['persistent'];
$this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
if ($this->database_name) {
if ($this->database_name != $this->connected_database_name) {
if (!@fbsql_select_db($this->database_name, $connection)) {
$err = $this->raiseError(null, null, null,
'Could not select the database: '.$this->database_name, __FUNCTION__);
return $err;
}
$this->connected_database_name = $this->database_name;
}
}
return MDB2_OK;
}
// }}}
// {{{ databaseExists()
/**
* check if given database name is exists?
*
* @param string $name name of the database that should be checked
*
* @return mixed true/false on success, a MDB2 error on failure
* @access public
*/
function databaseExists($name)
{
$connection = $this->_doConnect($this->dsn['username'],
$this->dsn['password'],
$this->options['persistent']);
if (MDB2::isError($connection)) {
return $connection;
}
$result = @fbsql_select_db($name, $connection);
@fbsql_close($connection);
return $result;
}
// }}}
// {{{ disconnect()
/**
* Log out and disconnect from the database.
*
* @param boolean $force if the disconnect should be forced even if the
* connection is opened persistently
* @return mixed true on success, false if not connected and error
* object on error
* @access public
*/
function disconnect($force = true)
{
if (is_resource($this->connection)) {
if ($this->in_transaction) {
$dsn = $this->dsn;
$database_name = $this->database_name;
$persistent = $this->options['persistent'];
$this->dsn = $this->connected_dsn;
$this->database_name = $this->connected_database_name;
$this->options['persistent'] = $this->opened_persistent;
$this->rollback();
$this->dsn = $dsn;
$this->database_name = $database_name;
$this->options['persistent'] = $persistent;
}
if (!$this->opened_persistent || $force) {
@fbsql_close($this->connection);
}
}
return parent::disconnect($force);
}
// }}}
// {{{ standaloneQuery()
/**
* execute a query as DBA
*
* @param string $query the SQL query
* @param mixed $types array that contains the types of the columns in
* the result set
* @param boolean $is_manip if the query is a manipulation query
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function &standaloneQuery($query, $types = null, $is_manip = false)
{
$user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username'];
$pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password'];
$connection = $this->_doConnect($user, $pass, $this->options['persistent']);
if (MDB2::isError($connection)) {
return $connection;
}
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
$result =& $this->_doQuery($query, $is_manip, $connection, $this->database_name);
if (!MDB2::isError($result)) {
$result = $this->_affectedRows($connection, $result);
}
@fbsql_close($connection);
return $result;
}
// }}}
// {{{ _doQuery()
/**
* Execute a query
* @param string $query query
* @param boolean $is_manip if the query is a manipulation query
* @param resource $connection
* @param string $database_name
* @return result or error object
* @access protected
*/
function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
{
$this->last_query = $query;
$result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (MDB2::isError($result)) {
return $result;
}
$query = $result;
}
if ($this->options['disable_query']) {
$result = $is_manip ? 0 : null;
return $result;
}
if (null === $connection) {
$connection = $this->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
}
if (null === $database_name) {
$database_name = $this->database_name;
}
if ($database_name) {
if ($database_name != $this->connected_database_name) {
if (!@fbsql_select_db($database_name, $connection)) {
$err = $this->raiseError(null, null, null,
'Could not select the database: '.$database_name, __FUNCTION__);
return $err;
}
$this->connected_database_name = $database_name;
}
}
$result = @fbsql_query($query, $connection);
if (!$result) {
$err =& $this->raiseError(null, null, null,
'Could not execute statement', __FUNCTION__);
return $err;
}
$this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
return $result;
}
// }}}
// {{{ _affectedRows()
/**
* Returns the number of rows affected
*
* @param resource $result
* @param resource $connection
* @return mixed MDB2 Error Object or the number of rows affected
* @access private
*/
function _affectedRows($connection, $result = null)
{
if (null === $connection) {
$connection = $this->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
}
return @fbsql_affected_rows($connection);
}
// }}}
// {{{ _modifyQuery()
/**
* Changes a query string for various DBMS specific reasons
*
* @param string $query query to modify
* @param boolean $is_manip if it is a DML query
* @param integer $limit limit the number of rows
* @param integer $offset start reading from given offset
* @return string modified query
* @access protected
*/
function _modifyQuery($query, $is_manip, $limit, $offset)
{
if ($limit > 0) {
if ($is_manip) {
return preg_replace('/^([\s(])*SELECT(?!\s*TOP\s*\()/i',
"\\1SELECT TOP($limit)", $query);
} else {
return preg_replace('/([\s(])*SELECT(?!\s*TOP\s*\()/i',
"\\1SELECT TOP($offset,$limit)", $query);
}
}
// Add ; to the end of the query. This is required by FrontBase
return $query.';';
}
// }}}
// {{{ nextID()
/**
* Returns the next free id of a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true the sequence is
* automatic created, if it
* not exists
*
* @return mixed MDB2 Error Object or id
* @access public
*/
function nextID($seq_name, $ondemand = true)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
$seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
$query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL);";
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$this->expectError(MDB2_ERROR_NOSUCHTABLE);
$result =& $this->_doQuery($query, true);
$this->popExpect();
$this->popErrorHandling();
if (MDB2::isError($result)) {
if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
$this->loadModule('Manager', null, true);
$result = $this->manager->createSequence($seq_name);
if (MDB2::isError($result)) {
return $this->raiseError($result, null, null,
'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
} else {
return $this->nextID($seq_name, false);
}
}
return $result;
}
$value = $this->lastInsertID();
if (is_numeric($value)) {
$query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
$result =& $this->_doQuery($query, true);
if (MDB2::isError($result)) {
$this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
}
}
return $value;
}
// }}}
// {{{ lastInsertID()
/**
* Returns the autoincrement ID if supported or $id or fetches the current
* ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
*
* @param string $table name of the table into which a new row was inserted
* @param string $field name of the field into which a new row was inserted
* @return mixed MDB2 Error Object or id
* @access public
*/
function lastInsertID($table = null, $field = null)
{
$connection = $this->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
$value = @fbsql_insert_id($connection);
if (!$value) {
return $this->raiseError(null, null, null,
'Could not get last insert ID', __FUNCTION__);
}
return $value;
}
// }}}
// {{{ currID()
/**
* Returns the current id of a sequence
*
* @param string $seq_name name of the sequence
* @return mixed MDB2 Error Object or id
* @access public
*/
function currID($seq_name)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
$seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
$query = "SELECT MAX($seqcol_name) FROM $sequence_name";
return $this->queryOne($query, 'integer');
}
}
/**
* MDB2 FrontbaseSQL result driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Result_fbsql extends MDB2_Result_Common
{
// }}}
// {{{ fetchRow()
/**
* Fetch a row and insert the data into an existing array.
*
* @param int $fetchmode how the array data should be indexed
* @param int $rownum number of the row where the data can be found
* @return int data array on success, a MDB2 error on failure
* @access public
*/
function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
{
if (null !== $rownum) {
$seek = $this->seek($rownum);
if (MDB2::isError($seek)) {
return $seek;
}
}
if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
$fetchmode = $this->db->fetchmode;
}
if ( $fetchmode == MDB2_FETCHMODE_ASSOC
|| $fetchmode == MDB2_FETCHMODE_OBJECT
) {
$row = @fbsql_fetch_assoc($this->result);
if (is_array($row)
&& $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
) {
$row = array_change_key_case($row, $this->db->options['field_case']);
}
} else {
$row = @fbsql_fetch_row($this->result);
}
if (!$row) {
if (false === $this->result) {
$err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
return $err;
}
$null = null;
return $null;
}
$mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
if ($mode) {
$this->db->_fixResultArrayValues($row, $mode);
}
if (($fetchmode != MDB2_FETCHMODE_ASSOC) && !empty($this->types)) {
$row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
} elseif (($fetchmode == MDB2_FETCHMODE_ASSOC) && !empty($this->types_assoc)) {
$row = $this->db->datatype->convertResultRow($this->types_assoc, $row, $rtrim);
}
if (!empty($this->values)) {
$this->_assignBindColumns($row);
}
if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
$object_class = $this->db->options['fetch_class'];
if ($object_class == 'stdClass') {
$row = (object) $row;
} else {
$row = new $object_class($row);
}
}
++$this->rownum;
return $row;
}
// }}}
// {{{ _getColumnNames()
/**
* Retrieve the names of columns returned by the DBMS in a query result.
*
* @return mixed Array variable that holds the names of columns as keys
* or an MDB2 error on failure.
* Some DBMS may not return any columns when the result set
* does not contain any rows.
* @access private
*/
function _getColumnNames()
{
$columns = array();
$numcols = $this->numCols();
if (MDB2::isError($numcols)) {
return $numcols;
}
for ($column = 0; $column < $numcols; $column++) {
$column_name = @fbsql_field_name($this->result, $column);
$columns[$column_name] = $column;
}
if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$columns = array_change_key_case($columns, $this->db->options['field_case']);
}
return $columns;
}
// }}}
// {{{ numCols()
/**
* Count the number of columns returned by the DBMS in a query result.
*
* @return mixed integer value with the number of columns, a MDB2 error
* on failure
* @access public
*/
function numCols()
{
$cols = @fbsql_num_fields($this->result);
if (null === $cols) {
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
if (null === $this->result) {
return count($this->types);
}
return $this->db->raiseError(null, null, null,
'Could not get column count', __FUNCTION__);
}
return $cols;
}
// }}}
// {{{ nextResult()
/**
* Move the internal result pointer to the next available result
*
* @return true on success, false if there is no more result set or an error object on failure
* @access public
*/
function nextResult()
{
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
if (null === $this->result) {
return false;
}
return @fbsql_next_result($this->result);
}
// }}}
// {{{ free()
/**
* Free the internal resources associated with result.
*
* @return boolean true on success, false if result is invalid
* @access public
*/
function free()
{
if (is_resource($this->result) && $this->db->connection) {
$free = @fbsql_free_result($this->result);
if (false === $free) {
return $this->db->raiseError(null, null, null,
'Could not free result', __FUNCTION__);
}
}
$this->result = false;
return MDB2_OK;
}
}
/**
* MDB2 FrontbaseSQL buffered result driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_BufferedResult_fbsql extends MDB2_Result_fbsql
{
// }}}
// {{{ seek()
/**
* Seek to a specific row in a result set
*
* @param int $rownum number of the row where the data can be found
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function seek($rownum = 0)
{
if ($this->rownum != ($rownum - 1) && !@fbsql_data_seek($this->result, $rownum)) {
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
if (null === $this->result) {
return MDB2_OK;
}
return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
}
$this->rownum = $rownum - 1;
return MDB2_OK;
}
// }}}
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return mixed true or false on sucess, a MDB2 error on failure
* @access public
*/
function valid()
{
$numrows = $this->numRows();
if (MDB2::isError($numrows)) {
return $numrows;
}
return $this->rownum < ($numrows - 1);
}
// }}}
// {{{ numRows()
/**
* Returns the number of rows in a result object
*
* @return mixed MDB2 Error Object or the number of rows
* @access public
*/
function numRows()
{
$rows = @fbsql_num_rows($this->result);
if (null === $rows) {
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
if (null === $this->result) {
return 0;
}
return $this->db->raiseError(null, null, null,
'Could not get row count', __FUNCTION__);
}
return $rows;
}
}
/**
* MDB2 FrontbaseSQL statement driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Statement_fbsql extends MDB2_Statement_Common
{
}
?>

1575
extlib/MDB2/Driver/ibase.php Normal file

File diff suppressed because it is too large Load Diff

1182
extlib/MDB2/Driver/mssql.php Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1667
extlib/MDB2/Driver/oci8.php Normal file

File diff suppressed because it is too large Load Diff

1142
extlib/MDB2/Driver/odbc.php Normal file

File diff suppressed because it is too large Load Diff

1584
extlib/MDB2/Driver/pgsql.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,824 @@
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Original QuerySim Concept & ColdFusion Author: Hal Helms |
// | <hal.helms@teamallaire.com> |
// | Bert Dawson <bdawson@redbanner.com> |
// +----------------------------------------------------------------------+
// | Original PHP Author: Alan Richmond <arichmond@bigfoot.com> |
// | David Huyck <b@bombusbee.com> |
// +----------------------------------------------------------------------+
// | Special note concerning code documentation: |
// | QuerySim was originally created for use during development of |
// | applications built using the Fusebox framework. (www.fusebox.org) |
// | Fusebox uses an XML style of documentation called Fusedoc. (Which |
// | is admittedly not well suited to documenting classes and functions. |
// | This short-coming is being addressed by the Fusebox community.) PEAR |
// | uses a Javadoc style of documentation called PHPDoc. (www.phpdoc.de) |
// | Since this class extension spans two groups of users, it is asked |
// | that the members of each respect the documentation standard of the |
// | other. So it is a further requirement that both documentation |
// | standards be included and maintained. If assistance is required |
// | please contact Alan Richmond. |
// +----------------------------------------------------------------------+
//
// $Id$
//
/*
<fusedoc fuse="querysim.php" language="PHP">
<responsibilities>
I take information and turn it into a recordset that can be accessed
through the PEAR MDB2 API. Based on Hal Helms' QuerySim.cfm ColdFusion
custom tag available at halhelms.com.
</responsibilities>
<properties>
<property name="API" value="PEAR MDB2" />
<property name="version" value="0.2.1" />
<property name="status" value="beta" />
<history author="Hal Helms" email="hal.helms@teamallaire.com" type="Create" />
<history author="Bert Dawson" email="bdawson@redbanner.com" type="Update">
Extensive revision that is backwardly compatible but eliminates the
need for a separate .sim file.
</history>
<history author="Alan Richmond" email="arichmond@bigfoot.com" type="Create" date="10-July-2002">
Rewrote in PHP as an extention to the PEAR DB API.
Functions supported:
connect, disconnect, query, fetchRow, freeResult,
numCols, numRows, getSpecialQuery
David Huyck (bombusbee.com) added ability to escape special
characters (i.e., delimiters) using a '\'.
Extended PEAR DB options[] for adding incoming parameters. Added
options: columnDelim, dataDelim, eolDelim
</history>
<history author="David Huyck" email="b@bombusbee.com" type="Update" date="19-July-2002">
Added the ability to set the QuerySim options at runtime.
Default options are:
'columnDelim' => ',', // Commas split the column names
'dataDelim' => '|', // Pipes split the data fields
'eolDelim' => chr(13).chr(10) // Carriage returns split the
// lines of data
Affected functions are:
DB_querysim(): set the default options when the
constructor method is called
_parseQuerySim($query): altered the parsing of lines, column
names, and data fields
_empty2null: altered the way this function is called
to simplify calling it
</history>
<history author="Alan Richmond" email="arichmond@bigfoot.com" type="Update" date="24-July-2002">
Added error catching for malformed QuerySim text.
Bug fix _empty2null(): altered version was returning unmodified
lineData.
Cleanup:
PEAR compliant formatting, finished PHPDocs and added 'out' to
Fusedoc 'io'.
Broke up _parseQuerySim() into _buildResult() and _parseOnDelim()
to containerize duplicate parse code.
</history>
<history author="David Huyck" email="b@bombusbee.com" type="Update" date="25-July-2002">
Edited the _buildResult() and _parseOnDelim() functions to improve
reliability of special character escaping.
Re-introduced a custom setOption() method to throw an error when a
person tries to set one of the delimiters to '\'.
</history>
<history author="Alan Richmond" email="arichmond@bigfoot.com" type="Update" date="27-July-2002">
Added '/' delimiter param to preg_quote() in _empty2null() and
_parseOnDelim() so '/' can be used as a delimiter.
Added error check for columnDelim == eolDelim or dataDelim == eolDelim.
Renamed some variables for consistancy.
</history>
<history author="Alan Richmond" email="arichmond@bigfoot.com" type="Update" date="30-July-2002">
Removed protected function _empty2null(). Turns out preg_split()
deals with empty elemants by making them zero length strings, just
what they ended up being anyway. This should speed things up a little.
Affected functions:
_parseOnDelim() perform trim on line here, instead of in
_empty2null().
_buildResult() remove call to _empty2null().
_empty2null() removed function.
</history>
<history author="Alan Richmond" email="arichmond@bigfoot.com" type="Update" date="1-Jan-2003">
Ported to PEAR MDB2.
Methods supported:
connect, query, getColumnNames, numCols, valid, fetch,
numRows, free, fetchRow, nextResult, setLimit
(inherited).
</history>
<history
Removed array_change_key_case() work around for <4.2.0 in
getColumnNames(), found it already done in MDB2/Common.php.
</history>
<history author="Alan Richmond" email="arichmond@bigfoot.com" type="Update" date="3-Feb-2003">
Changed default eolDelim to a *nix file eol, since we're trimming
the result anyway, it makes no difference for Windows. Now only
Mac file eols should need to be set (and other kinds of chars).
</history>
<note author="Alan Richmond">
Got WAY too long. See querysim_readme.txt for instructions and some
examples.
io section only documents elements of DB_result that DB_querysim uses,
adds or changes; see MDB2 and MDB2_Driver_Common for more info.
io section uses some elements that are not Fusedoc 2.0 compliant:
object and resource.
</note>
</properties>
<io>
<in>
<file path="MDB2/Common.php" action="require_once" />
</in>
<out>
<object name="MDB2_querysim" extends="MDB2_Driver_Common" instantiatedby="MDB2::connect()">
<resource type="file" name="connection" oncondition="source is external file" scope="class" />
<string name="phptype" default="querysim" />
<string name="dbsyntax" default="querysim" />
<array name="supported" comments="most of these don't actually do anything, they are enabled to simulate the option being available if checked">
<boolean name="sequences" default="true" />
<boolean name="indexes" default="true" />
<boolean name="affected_rows" default="true" />
<boolean name="summary_functions" default="true" />
<boolean name="order_by_text" default="true" />
<boolean name="current_id" default="true" />
<boolean name="limit_querys" default="true" comments="this one is functional" />
<boolean name="LOBs" default="true" />
<boolean name="replace" default="true" />
<boolean name="sub_selects" default="true" />
<boolean name="transactions" default="true" />
</array>
<string name="last_query" comments="last value passed in with query()" />
<array name="options" comments="these can be changed at run time">
<string name="columnDelim" default="," />
<string name="dataDelim" default="|" />
<string name="eolDelim" default="chr(13).chr(10)" />
</array>
</object>
<array name="result" comments="the simulated record set returned by ::query()">
<array comments="columns">
<string comments="column name" />
</array>
<array comments="data">
<array comments="row">
<string comments="data element" />
</array>
</array>
</array>
</out>
</io>
</fusedoc>
*/
/**
* MDB2 QuerySim driver
*
* @package MDB2
* @category Database
* @author Alan Richmond <arichmond@bigfoot.com>
*/
class MDB2_Driver_querysim extends MDB2_Driver_Common
{
// }}}
// {{{ constructor
/**
* Constructor
*/
function __construct()
{
parent::__construct();
$this->phptype = 'querysim';
$this->dbsyntax = 'querysim';
// Most of these are dummies to simulate availability if checked
$this->supported['sequences'] = false;
$this->supported['indexes'] = false;
$this->supported['affected_rows'] = false;
$this->supported['summary_functions'] = false;
$this->supported['order_by_text'] = false;
$this->supported['current_id'] = false;
$this->supported['limit_queries'] = true;// this one is real
$this->supported['LOBs'] = true;
$this->supported['replace'] = false;
$this->supported['sub_selects'] = false;
$this->supported['transactions'] = false;
$this->supported['savepoints'] = false;
$this->supported['auto_increment'] = false;
$this->supported['primary_key'] = false;
$this->supported['result_introspection'] = false; // not implemented
$this->supported['prepared_statements'] = false;
$this->supported['identifier_quoting'] = false;
$this->supported['pattern_escaping'] = false;
$this->supported['new_link'] = false;
$this->options['columnDelim'] = ',';
$this->options['dataDelim'] = '|';
$this->options['eolDelim'] = "\n";
}
// }}}
// {{{ connect()
/**
* Open a file or simulate a successful database connect
*
* @access public
*
* @return mixed MDB2_OK string on success, a MDB2 error object on failure
*/
function connect()
{
if (is_resource($this->connection)) {
if ($this->connected_database_name == $this->database_name
&& ($this->opened_persistent == $this->options['persistent'])
) {
return MDB2_OK;
}
if ($this->connected_database_name) {
$this->_close($this->connection);
}
$this->disconnect();
}
$connection = 1;// sim connect
// if external, check file...
if ($this->database_name) {
$file = $this->database_name;
if (!file_exists($file)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'file not found', __FUNCTION__);
}
if (!is_file($file)) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'not a file', __FUNCTION__);
}
if (!is_readable($file)) {
return $this->raiseError(MDB2_ERROR_ACCESS_VIOLATION, null, null,
'could not open file - check permissions', __FUNCTION__);
}
// ...and open if persistent
if ($this->options['persistent']) {
$connection = @fopen($file, 'r');
}
}
if (!empty($this->dsn['charset'])) {
$result = $this->setCharset($this->dsn['charset'], $connection);
if (MDB2::isError($result)) {
return $result;
}
}
$this->connection = $connection;
$this->connected_database_name = $this->database_name;
$this->opened_persistent = $this->options['persistent'];
$this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
return MDB2_OK;
}
// }}}
// {{{ disconnect()
/**
* Log out and disconnect from the database.
*
* @param boolean $force if the disconnect should be forced even if the
* connection is opened persistently
* @return mixed true on success, false if not connected and error
* object on error
* @access public
*/
function disconnect($force = true)
{
if (is_resource($this->connection)) {
if ($this->opened_persistent) {
@fclose($this->connection);
}
}
return parent::disconnect($force);
}
// }}}
// {{{ _doQuery()
/**
* Execute a query
* @param string $query query
* @param boolean $is_manip if the query is a manipulation query
* @param resource $connection
* @param string $database_name
* @return result or error object
* @access protected
*/
function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
{
if ($this->database_name) {
$query = $this->_readFile();
}
$this->last_query = $query;
$result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (MDB2::isError($result)) {
return $result;
}
$query = $result;
}
if ($is_manip) {
$err =& $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'Manipulation statements are not supported', __FUNCTION__);
return $err;
}
if ($this->options['disable_query']) {
$result = $is_manip ? 0 : null;
return $result;
}
$result = $this->_buildResult($query);
if (MDB2::isError($result)) {
return $result;
}
if ($this->next_limit > 0) {
$result[1] = array_slice($result[1], $this->next_offset, $this->next_limit);
}
$this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
return $result;
}
// }}}
// {{{ _modifyQuery()
/**
* Changes a query string for various DBMS specific reasons
*
* @param string $query query to modify
* @param boolean $is_manip if it is a DML query
* @param integer $limit limit the number of rows
* @param integer $offset start reading from given offset
* @return string modified query
* @access protected
*/
function _modifyQuery($query, $is_manip, $limit, $offset)
{
$this->next_limit = $limit;
$this->next_offset = $offset;
return $query;
}
// }}}
// {{{ _readFile()
/**
* Read an external file
*
* @param string filepath/filename
*
* @access protected
*
* @return string the contents of a file
*/
function _readFile()
{
$buffer = '';
if ($this->opened_persistent) {
$connection = $this->getConnection();
if (MDB2::isError($connection)) {
return $connection;
}
while (!feof($connection)) {
$buffer.= fgets($connection, 1024);
}
rewind($connection);
} else {
$connection = @fopen($this->connected_database_name, 'r');
while (!feof($connection)) {
$buffer.= fgets($connection, 1024);
}
@fclose($connection);
}
return $buffer;
}
// }}}
// {{{ _buildResult()
/**
* Convert QuerySim text into an array
*
* @param string Text of simulated query
*
* @access protected
*
* @return multi-dimensional array containing the column names and data
* from the QuerySim
*/
function _buildResult($query)
{
$eolDelim = $this->options['eolDelim'];
$columnDelim = $this->options['columnDelim'];
$dataDelim = $this->options['dataDelim'];
$columnNames = array();
$data = array();
if ($columnDelim == $eolDelim) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'columnDelim and eolDelim must be different', __FUNCTION__);
} elseif ($dataDelim == $eolDelim){
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'dataDelim and eolDelim must be different', __FUNCTION__);
}
$query = trim($query);
//tokenize escaped slashes
$query = str_replace('\\\\', '[$double-slash$]', $query);
if (!strlen($query)) {
return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'empty querysim text', __FUNCTION__);
}
$lineData = $this->_parseOnDelim($query, $eolDelim);
//kill the empty last row created by final eol char if it exists
if (!strlen(trim($lineData[count($lineData) - 1]))) {
unset($lineData[count($lineData) - 1]);
}
//populate columnNames array
$thisLine = each($lineData);
$columnNames = $this->_parseOnDelim($thisLine[1], $columnDelim);
if ((in_array('', $columnNames)) || (in_array('NULL', $columnNames))) {
return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'all column names must be defined', __FUNCTION__);
}
//replace double-slash tokens with single-slash
$columnNames = str_replace('[$double-slash$]', '\\', $columnNames);
$columnCount = count($columnNames);
$rowNum = 0;
//loop through data lines
if (count($lineData) > 1) {
while ($thisLine = each($lineData)) {
$thisData = $this->_parseOnDelim($thisLine[1], $dataDelim);
$thisDataCount = count($thisData);
if ($thisDataCount != $columnCount) {
$fileLineNo = $rowNum + 2;
return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
"number of data elements ($thisDataCount) in line $fileLineNo not equal to number of defined columns ($columnCount)", __FUNCTION__);
}
//loop through data elements in data line
foreach ($thisData as $thisElement) {
if (strtoupper($thisElement) == 'NULL'){
$thisElement = '';
}
//replace double-slash tokens with single-slash
$data[$rowNum][] = str_replace('[$double-slash$]', '\\', $thisElement);
}//end foreach
++$rowNum;
}//end while
}//end if
return array($columnNames, $data);
}
// }}}
// {{{ _parseOnDelim()
/**
* Split QuerySim string into an array on a delimiter
*
* @param string $thisLine Text of simulated query
* @param string $delim The delimiter to split on
*
* @access protected
*
* @return array containing parsed string
*/
function _parseOnDelim($thisLine, $delim)
{
$delimQuoted = preg_quote($delim, '/');
$thisLine = trim($thisLine);
$parsed = preg_split('/(?<!\\\\)' .$delimQuoted. '/', $thisLine);
//replaces escaped delimiters
$parsed = preg_replace('/\\\\' .$delimQuoted. '/', $delim, $parsed);
if ($delim != $this->options['eolDelim']) {
//replaces escape chars
$parsed = preg_replace('/\\\\/', '', $parsed);
}
return $parsed;
}
// }}}
// {{{ &exec()
/**
* Execute a manipulation query to the database and return any the affected rows
*
* @param string $query the SQL query
* @return mixed affected rows on success, a MDB2 error on failure
* @access public
*/
function &exec($query)
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'Querysim only supports reading data', __FUNCTION__);
}
// }}}
// {{{ getServerVersion()
/**
* return version information about the server
*
* @param bool $native determines if the raw version string should be returned
* @return mixed array/string with version information or MDB2 error object
* @access public
*/
function getServerVersion($native = false)
{
$server_info = '@package_version@';
// cache server_info
$this->connected_server_info = $server_info;
if (!$native) {
$tmp = explode('.', $server_info, 3);
if (!isset($tmp[2]) || !preg_match('/(\d+)(.*)/', $tmp[2], $tmp2)) {
$tmp2[0] = isset($tmp[2]) ? $tmp[2] : null;
$tmp2[1] = null;
}
$server_info = array(
'major' => isset($tmp[0]) ? $tmp[0] : null,
'minor' => isset($tmp[1]) ? $tmp[1] : null,
'patch' => isset($tmp2[0]) ? $tmp2[0] : null,
'extra' => isset($tmp2[1]) ? $tmp2[1] : null,
'native' => $server_info,
);
}
return $server_info;
}
}
/**
* MDB2 QuerySim result driver
*
* @package MDB2
* @category Database
* @author Alan Richmond <arichmond@bigfoot.com>
*/
class MDB2_Result_querysim extends MDB2_Result_Common
{
// }}}
// {{{ fetchRow()
/**
* Fetch a row and insert the data into an existing array.
*
* @param int $fetchmode how the array data should be indexed
* @param int $rownum number of the row where the data can be found
* @return int data array on success, a MDB2 error on failure
* @access public
*/
function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
{
if (false === $this->result) {
$err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
return $err;
}
if (null === $this->result) {
return null;
}
if (null !== $rownum) {
$seek = $this->seek($rownum);
if (MDB2::isError($seek)) {
return $seek;
}
}
$target_rownum = $this->rownum + 1;
if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
$fetchmode = $this->db->fetchmode;
}
if (!isset($this->result[1][$target_rownum])) {
$null = null;
return $null;
}
$row = $this->result[1][$target_rownum];
// make row associative
if ( $fetchmode == MDB2_FETCHMODE_ASSOC
|| $fetchmode == MDB2_FETCHMODE_OBJECT
) {
$column_names = $this->getColumnNames();
foreach ($column_names as $name => $i) {
$column_names[$name] = $row[$i];
}
$row = $column_names;
}
$mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
$rtrim = false;
if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
if (empty($this->types)) {
$mode += MDB2_PORTABILITY_RTRIM;
} else {
$rtrim = true;
}
}
if ($mode) {
$this->db->_fixResultArrayValues($row, $mode);
}
if ( ( $fetchmode != MDB2_FETCHMODE_ASSOC
&& $fetchmode != MDB2_FETCHMODE_OBJECT)
&& !empty($this->types)
) {
$row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
} elseif (($fetchmode == MDB2_FETCHMODE_ASSOC
|| $detchmode == MDB2_FETCHMODE_OBJECT)
&& !empty($this->types_assoc)
) {
$row = $this->db->datatype->convertResultRow($this->types_assoc, $row, $rtrim);
}
if (!empty($this->values)) {
$this->_assignBindColumns($row);
}
if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
$object_class = $this->db->options['fetch_class'];
if ($object_class == 'stdClass') {
$row = (object) $row;
} else {
$row = new $object_class($row);
}
}
++$this->rownum;
return $row;
}
// }}}
// {{{ _getColumnNames()
/**
* Retrieve the names of columns returned by the DBMS in a query result.
*
* @return mixed Array variable that holds the names of columns as keys
* or an MDB2 error on failure.
* Some DBMS may not return any columns when the result set
* does not contain any rows.
* @access private
*/
function _getColumnNames()
{
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
if (null === $this->result) {
return array();
}
$columns = array_flip($this->result[0]);
if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$columns = array_change_key_case($columns, $this->db->options['field_case']);
}
return $columns;
}
// }}}
// {{{ numCols()
/**
* Count the number of columns returned by the DBMS in a query result.
*
* @access public
* @return mixed integer value with the number of columns, a MDB2 error
* on failure
*/
function numCols()
{
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
if (null === $this->result) {
return count($this->types);
}
$cols = count($this->result[0]);
return $cols;
}
}
/**
* MDB2 QuerySim buffered result driver
*
* @package MDB2
* @category Database
* @author Alan Richmond <arichmond@bigfoot.com>
*/
class MDB2_BufferedResult_querysim extends MDB2_Result_querysim
{
// }}}
// {{{ seek()
/**
* Seek to a specific row in a result set
*
* @param int $rownum number of the row where the data can be found
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function seek($rownum = 0)
{
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
$this->rownum = $rownum - 1;
return MDB2_OK;
}
// }}}
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return mixed true or false on sucess, a MDB2 error on failure
* @access public
*/
function valid()
{
$numrows = $this->numRows();
if (MDB2::isError($numrows)) {
return $numrows;
}
return $this->rownum < ($numrows - 1);
}
// }}}
// {{{ numRows()
/**
* Returns the number of rows in a result object
*
* @return mixed MDB2 Error Object or the number of rows
* @access public
*/
function numRows()
{
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
}
if (null === $this->result) {
return 0;
}
$rows = count($this->result[1]);
return $rows;
}
}
/**
* MDB2 QuerySim statement driver
*
* @package MDB2
* @category Database
* @author Alan Richmond <arichmond@bigfoot.com>
*/
class MDB2_Statement_querysim extends MDB2_Statement_Common
{
}
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

723
extlib/MDB2/Extended.php Normal file
View File

@ -0,0 +1,723 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
/**
* Used by autoPrepare()
*/
define('MDB2_AUTOQUERY_INSERT', 1);
define('MDB2_AUTOQUERY_UPDATE', 2);
define('MDB2_AUTOQUERY_DELETE', 3);
define('MDB2_AUTOQUERY_SELECT', 4);
/**
* MDB2_Extended: class which adds several high level methods to MDB2
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Extended extends MDB2_Module_Common
{
// {{{ autoPrepare()
/**
* Generate an insert, update or delete query and call prepare() on it
*
* @param string table
* @param array the fields names
* @param int type of query to build
* MDB2_AUTOQUERY_INSERT
* MDB2_AUTOQUERY_UPDATE
* MDB2_AUTOQUERY_DELETE
* MDB2_AUTOQUERY_SELECT
* @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
* @param array that contains the types of the placeholders
* @param mixed array that contains the types of the columns in
* the result set or MDB2_PREPARE_RESULT, if set to
* MDB2_PREPARE_MANIP the query is handled as a manipulation query
*
* @return resource handle for the query
* @see buildManipSQL
* @access public
*/
function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT,
$where = false, $types = null, $result_types = MDB2_PREPARE_MANIP)
{
$query = $this->buildManipSQL($table, $table_fields, $mode, $where);
if (MDB2::isError($query)) {
return $query;
}
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$lobs = array();
foreach ((array)$types as $param => $type) {
if (($type == 'clob') || ($type == 'blob')) {
$lobs[$param] = $table_fields[$param];
}
}
return $db->prepare($query, $types, $result_types, $lobs);
}
// }}}
// {{{ autoExecute()
/**
* Generate an insert, update or delete query and call prepare() and execute() on it
*
* @param string name of the table
* @param array assoc ($key=>$value) where $key is a field name and $value its value
* @param int type of query to build
* MDB2_AUTOQUERY_INSERT
* MDB2_AUTOQUERY_UPDATE
* MDB2_AUTOQUERY_DELETE
* MDB2_AUTOQUERY_SELECT
* @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
* @param array that contains the types of the placeholders
* @param string which specifies which result class to use
* @param mixed array that contains the types of the columns in
* the result set or MDB2_PREPARE_RESULT, if set to
* MDB2_PREPARE_MANIP the query is handled as a manipulation query
*
* @return bool|MDB2_Error true on success, a MDB2 error on failure
* @see buildManipSQL
* @see autoPrepare
* @access public
*/
function autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT,
$where = false, $types = null, $result_class = true, $result_types = MDB2_PREPARE_MANIP)
{
$fields_values = (array)$fields_values;
if ($mode == MDB2_AUTOQUERY_SELECT) {
if (is_array($result_types)) {
$keys = array_keys($result_types);
} elseif (!empty($fields_values)) {
$keys = $fields_values;
} else {
$keys = array();
}
} else {
$keys = array_keys($fields_values);
}
$params = array_values($fields_values);
if (empty($params)) {
$query = $this->buildManipSQL($table, $keys, $mode, $where);
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if ($mode == MDB2_AUTOQUERY_SELECT) {
$result = $db->query($query, $result_types, $result_class);
} else {
$result = $db->exec($query);
}
} else {
$stmt = $this->autoPrepare($table, $keys, $mode, $where, $types, $result_types);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params, $result_class);
$stmt->free();
}
return $result;
}
// }}}
// {{{ buildManipSQL()
/**
* Make automaticaly an sql query for prepare()
*
* Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), MDB2_AUTOQUERY_INSERT)
* will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
* NB : - This belongs more to a SQL Builder class, but this is a simple facility
* - Be carefull ! If you don't give a $where param with an UPDATE/DELETE query, all
* the records of the table will be updated/deleted !
*
* @param string name of the table
* @param ordered array containing the fields names
* @param int type of query to build
* MDB2_AUTOQUERY_INSERT
* MDB2_AUTOQUERY_UPDATE
* MDB2_AUTOQUERY_DELETE
* MDB2_AUTOQUERY_SELECT
* @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
*
* @return string sql query for prepare()
* @access public
*/
function buildManipSQL($table, $table_fields, $mode, $where = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if ($db->options['quote_identifier']) {
$table = $db->quoteIdentifier($table);
}
if (!empty($table_fields) && $db->options['quote_identifier']) {
foreach ($table_fields as $key => $field) {
$table_fields[$key] = $db->quoteIdentifier($field);
}
}
if ((false !== $where) && (null !== $where)) {
if (is_array($where)) {
$where = implode(' AND ', $where);
}
$where = ' WHERE '.$where;
}
switch ($mode) {
case MDB2_AUTOQUERY_INSERT:
if (empty($table_fields)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Insert requires table fields', __FUNCTION__);
}
$cols = implode(', ', $table_fields);
$values = '?'.str_repeat(', ?', (count($table_fields) - 1));
return 'INSERT INTO '.$table.' ('.$cols.') VALUES ('.$values.')';
break;
case MDB2_AUTOQUERY_UPDATE:
if (empty($table_fields)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Update requires table fields', __FUNCTION__);
}
$set = implode(' = ?, ', $table_fields).' = ?';
$sql = 'UPDATE '.$table.' SET '.$set.$where;
return $sql;
break;
case MDB2_AUTOQUERY_DELETE:
$sql = 'DELETE FROM '.$table.$where;
return $sql;
break;
case MDB2_AUTOQUERY_SELECT:
$cols = !empty($table_fields) ? implode(', ', $table_fields) : '*';
$sql = 'SELECT '.$cols.' FROM '.$table.$where;
return $sql;
break;
}
return $db->raiseError(MDB2_ERROR_SYNTAX, null, null,
'Non existant mode', __FUNCTION__);
}
// }}}
// {{{ limitQuery()
/**
* Generates a limited query
*
* @param string query
* @param array that contains the types of the columns in the result set
* @param integer the numbers of rows to fetch
* @param integer the row to start to fetching
* @param string which specifies which result class to use
* @param mixed string which specifies which class to wrap results in
*
* @return MDB2_Result|MDB2_Error result set on success, a MDB2 error on failure
* @access public
*/
function limitQuery($query, $types, $limit, $offset = 0, $result_class = true,
$result_wrap_class = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
$result = $db->setLimit($limit, $offset);
if (MDB2::isError($result)) {
return $result;
}
return $db->query($query, $types, $result_class, $result_wrap_class);
}
// }}}
// {{{ execParam()
/**
* Execute a parameterized DML statement.
*
* @param string the SQL query
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
*
* @return int|MDB2_Error affected rows on success, a MDB2 error on failure
* @access public
*/
function execParam($query, $params = array(), $param_types = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->exec($query);
}
$stmt = $db->prepare($query, $param_types, MDB2_PREPARE_MANIP);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (MDB2::isError($result)) {
return $result;
}
$stmt->free();
return $result;
}
// }}}
// {{{ getOne()
/**
* Fetch the first column of the first row of data returned from a query.
* Takes care of doing the query and freeing the results when finished.
*
* @param string the SQL query
* @param string that contains the type of the column in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int|string which column to return
*
* @return scalar|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getOne($query, $type = null, $params = array(),
$param_types = null, $colnum = 0)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
settype($params, 'array');
settype($type, 'array');
if (empty($params)) {
return $db->queryOne($query, $type, $colnum);
}
$stmt = $db->prepare($query, $param_types, $type);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$one = $result->fetchOne($colnum);
$stmt->free();
$result->free();
return $one;
}
// }}}
// {{{ getRow()
/**
* Fetch the first row of data returned from a query. Takes care
* of doing the query and freeing the results when finished.
*
* @param string the SQL query
* @param array that contains the types of the columns in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int the fetch mode to use
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getRow($query, $types = null, $params = array(),
$param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->queryRow($query, $types, $fetchmode);
}
$stmt = $db->prepare($query, $param_types, $types);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$row = $result->fetchRow($fetchmode);
$stmt->free();
$result->free();
return $row;
}
// }}}
// {{{ getCol()
/**
* Fetch a single column from a result set and return it as an
* indexed array.
*
* @param string the SQL query
* @param string that contains the type of the column in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int|string which column to return
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getCol($query, $type = null, $params = array(),
$param_types = null, $colnum = 0)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
settype($params, 'array');
settype($type, 'array');
if (empty($params)) {
return $db->queryCol($query, $type, $colnum);
}
$stmt = $db->prepare($query, $param_types, $type);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$col = $result->fetchCol($colnum);
$stmt->free();
$result->free();
return $col;
}
// }}}
// {{{ getAll()
/**
* Fetch all the rows returned from a query.
*
* @param string the SQL query
* @param array that contains the types of the columns in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int the fetch mode to use
* @param bool if set to true, the $all will have the first
* column as its first dimension
* @param bool $force_array used only when the query returns exactly
* two columns. If true, the values of the returned array will be
* one-element arrays instead of scalars.
* @param bool $group if true, the values of the returned array is
* wrapped in another array. If the same key value (in the first
* column) repeats itself, the values will be appended to this array
* instead of overwriting the existing values.
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getAll($query, $types = null, $params = array(),
$param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
$rekey = false, $force_array = false, $group = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->queryAll($query, $types, $fetchmode, $rekey, $force_array, $group);
}
$stmt = $db->prepare($query, $param_types, $types);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
$stmt->free();
$result->free();
return $all;
}
// }}}
// {{{ getAssoc()
/**
* Fetch the entire result set of a query and return it as an
* associative array using the first column as the key.
*
* If the result set contains more than two columns, the value
* will be an array of the values from column 2-n. If the result
* set contains only two columns, the returned value will be a
* scalar with the value of the second column (unless forced to an
* array with the $force_array parameter). A MDB2 error code is
* returned on errors. If the result set contains fewer than two
* columns, a MDB2_ERROR_TRUNCATED error is returned.
*
* For example, if the table 'mytable' contains:
* <pre>
* ID TEXT DATE
* --------------------------------
* 1 'one' 944679408
* 2 'two' 944679408
* 3 'three' 944679408
* </pre>
* Then the call getAssoc('SELECT id,text FROM mytable') returns:
* <pre>
* array(
* '1' => 'one',
* '2' => 'two',
* '3' => 'three',
* )
* </pre>
* ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
* <pre>
* array(
* '1' => array('one', '944679408'),
* '2' => array('two', '944679408'),
* '3' => array('three', '944679408')
* )
* </pre>
*
* If the more than one row occurs with the same value in the
* first column, the last row overwrites all previous ones by
* default. Use the $group parameter if you don't want to
* overwrite like this. Example:
* <pre>
* getAssoc('SELECT category,id,name FROM mytable', null, null
* MDB2_FETCHMODE_ASSOC, false, true) returns:
* array(
* '1' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* ),
* '9' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* )
* )
* </pre>
*
* Keep in mind that database functions in PHP usually return string
* values for results regardless of the database's internal type.
*
* @param string the SQL query
* @param array that contains the types of the columns in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param bool $force_array used only when the query returns
* exactly two columns. If TRUE, the values of the returned array
* will be one-element arrays instead of scalars.
* @param bool $group if TRUE, the values of the returned array
* is wrapped in another array. If the same key value (in the first
* column) repeats itself, the values will be appended to this array
* instead of overwriting the existing values.
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getAssoc($query, $types = null, $params = array(), $param_types = null,
$fetchmode = MDB2_FETCHMODE_DEFAULT, $force_array = false, $group = false)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->queryAll($query, $types, $fetchmode, true, $force_array, $group);
}
$stmt = $db->prepare($query, $param_types, $types);
if (MDB2::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$all = $result->fetchAll($fetchmode, true, $force_array, $group);
$stmt->free();
$result->free();
return $all;
}
// }}}
// {{{ executeMultiple()
/**
* This function does several execute() calls on the same statement handle.
* $params must be an array indexed numerically from 0, one execute call is
* done for every 'row' in the array.
*
* If an error occurs during execute(), executeMultiple() does not execute
* the unfinished rows, but rather returns that error.
*
* @param resource query handle from prepare()
* @param array numeric array containing the data to insert into the query
*
* @return bool|MDB2_Error true on success, a MDB2 error on failure
* @access public
* @see prepare(), execute()
*/
function executeMultiple($stmt, $params = null)
{
if (MDB2::isError($stmt)) {
return $stmt;
}
for ($i = 0, $j = count($params); $i < $j; $i++) {
$result = $stmt->execute($params[$i]);
if (MDB2::isError($result)) {
return $result;
}
}
return MDB2_OK;
}
// }}}
// {{{ getBeforeID()
/**
* Returns the next free id of a sequence if the RDBMS
* does not support auto increment
*
* @param string name of the table into which a new row was inserted
* @param string name of the field into which a new row was inserted
* @param bool when true the sequence is automatic created, if it not exists
* @param bool if the returned value should be quoted
*
* @return int|MDB2_Error id on success, a MDB2 error on failure
* @access public
*/
function getBeforeID($table, $field = null, $ondemand = true, $quote = true)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if ($db->supports('auto_increment') !== true) {
$seq = $table.(empty($field) ? '' : '_'.$field);
$id = $db->nextID($seq, $ondemand);
if (!$quote || MDB2::isError($id)) {
return $id;
}
return $db->quote($id, 'integer');
} elseif (!$quote) {
return null;
}
return 'NULL';
}
// }}}
// {{{ getAfterID()
/**
* Returns the autoincrement ID if supported or $id
*
* @param mixed value as returned by getBeforeId()
* @param string name of the table into which a new row was inserted
* @param string name of the field into which a new row was inserted
*
* @return int|MDB2_Error id on success, a MDB2 error on failure
* @access public
*/
function getAfterID($id, $table, $field = null)
{
$db = $this->getDBInstance();
if (MDB2::isError($db)) {
return $db;
}
if ($db->supports('auto_increment') !== true) {
return $id;
}
return $db->lastInsertID($table, $field);
}
// }}}
}
?>

262
extlib/MDB2/Iterator.php Normal file
View File

@ -0,0 +1,262 @@
<?php
// +----------------------------------------------------------------------+
// | PHP version 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
/**
* PHP5 Iterator
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Iterator implements Iterator
{
protected $fetchmode;
/**
* @var MDB2_Result_Common
*/
protected $result;
protected $row;
// {{{ constructor
/**
* Constructor
*/
public function __construct(MDB2_Result_Common $result, $fetchmode = MDB2_FETCHMODE_DEFAULT)
{
$this->result = $result;
$this->fetchmode = $fetchmode;
}
// }}}
// {{{ seek()
/**
* Seek forward to a specific row in a result set
*
* @param int number of the row where the data can be found
*
* @return void
* @access public
*/
public function seek($rownum)
{
$this->row = null;
if ($this->result) {
$this->result->seek($rownum);
}
}
// }}}
// {{{ next()
/**
* Fetch next row of data
*
* @return void
* @access public
*/
public function next()
{
$this->row = null;
}
// }}}
// {{{ current()
/**
* return a row of data
*
* @return void
* @access public
*/
public function current()
{
if (null === $this->row) {
$row = $this->result->fetchRow($this->fetchmode);
if (MDB2::isError($row)) {
$row = false;
}
$this->row = $row;
}
return $this->row;
}
// }}}
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return bool true/false, false is also returned on failure
* @access public
*/
public function valid()
{
return (bool)$this->current();
}
// }}}
// {{{ free()
/**
* Free the internal resources associated with result.
*
* @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
* @access public
*/
public function free()
{
if ($this->result) {
return $this->result->free();
}
$this->result = false;
$this->row = null;
return false;
}
// }}}
// {{{ key()
/**
* Returns the row number
*
* @return int|bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
* @access public
*/
public function key()
{
if ($this->result) {
return $this->result->rowCount();
}
return false;
}
// }}}
// {{{ rewind()
/**
* Seek to the first row in a result set
*
* @return void
* @access public
*/
public function rewind()
{
}
// }}}
// {{{ destructor
/**
* Destructor
*/
public function __destruct()
{
$this->free();
}
// }}}
}
/**
* PHP5 buffered Iterator
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_BufferedIterator extends MDB2_Iterator implements SeekableIterator
{
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
* @access public
*/
public function valid()
{
if ($this->result) {
return $this->result->valid();
}
return false;
}
// }}}
// {{{count()
/**
* Returns the number of rows in a result object
*
* @return int|MDB2_Error number of rows, false|MDB2_Error if result is invalid
* @access public
*/
public function count()
{
if ($this->result) {
return $this->result->numRows();
}
return false;
}
// }}}
// {{{ rewind()
/**
* Seek to the first row in a result set
*
* @return void
* @access public
*/
public function rewind()
{
$this->seek(0);
}
// }}}
}
?>

264
extlib/MDB2/LOB.php Normal file
View File

@ -0,0 +1,264 @@
<?php
// +----------------------------------------------------------------------+
// | PHP version 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id$
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
require_once 'MDB2.php';
/**
* MDB2_LOB: user land stream wrapper implementation for LOB support
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_LOB
{
/**
* contains the key to the global MDB2 instance array of the associated
* MDB2 instance
*
* @var integer
* @access protected
*/
var $db_index;
/**
* contains the key to the global MDB2_LOB instance array of the associated
* MDB2_LOB instance
*
* @var integer
* @access protected
*/
var $lob_index;
// {{{ stream_open()
/**
* open stream
*
* @param string specifies the URL that was passed to fopen()
* @param string the mode used to open the file
* @param int holds additional flags set by the streams API
* @param string not used
*
* @return bool
* @access public
*/
function stream_open($path, $mode, $options, &$opened_path)
{
if (!preg_match('/^rb?\+?$/', $mode)) {
return false;
}
$url = parse_url($path);
if (empty($url['host'])) {
return false;
}
$this->db_index = (int)$url['host'];
if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
return false;
}
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
$this->lob_index = (int)$url['user'];
if (!isset($db->datatype->lobs[$this->lob_index])) {
return false;
}
return true;
}
// }}}
// {{{ stream_read()
/**
* read stream
*
* @param int number of bytes to read
*
* @return string
* @access public
*/
function stream_read($count)
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
$db->datatype->_retrieveLOB($db->datatype->lobs[$this->lob_index]);
$data = $db->datatype->_readLOB($db->datatype->lobs[$this->lob_index], $count);
$length = strlen($data);
if ($length == 0) {
$db->datatype->lobs[$this->lob_index]['endOfLOB'] = true;
}
$db->datatype->lobs[$this->lob_index]['position'] += $length;
return $data;
}
}
// }}}
// {{{ stream_write()
/**
* write stream, note implemented
*
* @param string data
*
* @return int
* @access public
*/
function stream_write($data)
{
return 0;
}
// }}}
// {{{ stream_tell()
/**
* return the current position
*
* @return int current position
* @access public
*/
function stream_tell()
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
return $db->datatype->lobs[$this->lob_index]['position'];
}
}
// }}}
// {{{ stream_eof()
/**
* Check if stream reaches EOF
*
* @return bool
* @access public
*/
function stream_eof()
{
if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
return true;
}
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
$result = $db->datatype->_endOfLOB($db->datatype->lobs[$this->lob_index]);
if (version_compare(phpversion(), "5.0", ">=")
&& version_compare(phpversion(), "5.1", "<")
) {
return !$result;
}
return $result;
}
// }}}
// {{{ stream_seek()
/**
* Seek stream, not implemented
*
* @param int offset
* @param int whence
*
* @return bool
* @access public
*/
function stream_seek($offset, $whence)
{
return false;
}
// }}}
// {{{ stream_stat()
/**
* return information about stream
*
* @access public
*/
function stream_stat()
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
return array(
'db_index' => $this->db_index,
'lob_index' => $this->lob_index,
);
}
}
// }}}
// {{{ stream_close()
/**
* close stream
*
* @access public
*/
function stream_close()
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
if (isset($db->datatype->lobs[$this->lob_index])) {
$db->datatype->_destroyLOB($db->datatype->lobs[$this->lob_index]);
unset($db->datatype->lobs[$this->lob_index]);
}
}
}
// }}}
}
// register streams wrapper
if (!stream_wrapper_register("MDB2LOB", "MDB2_LOB")) {
MDB2::raiseError();
return false;
}
?>

View File

@ -220,8 +220,8 @@ class MysqlSchema extends Schema
*/
public function fetchMetaInfo($table, $infoTable, $orderBy = null)
{
$schema = $this->conn->dsn['database'];
return $this->fetchQueryData(sprintf(
$schema = $this->conn->getDatabase();
$info = $this->fetchQueryData(sprintf(
<<<'END'
SELECT * FROM INFORMATION_SCHEMA.%1$s
WHERE TABLE_SCHEMA = '%2$s' AND TABLE_NAME = '%3$s'%4$s;
@ -231,6 +231,10 @@ class MysqlSchema extends Schema
$table,
($orderBy ? " ORDER BY {$orderBy}" : '')
));
return array_map(function (array $cols): array {
return array_change_key_case($cols, CASE_UPPER);
}, $info);
}
/**
@ -242,7 +246,7 @@ class MysqlSchema extends Schema
*/
private function fetchKeyInfo(string $table): array
{
$schema = $this->conn->dsn['database'];
$schema = $this->conn->getDatabase();
$data = $this->fetchQueryData(
<<<EOT
SELECT INDEX_NAME AS `key_name`,
@ -290,7 +294,7 @@ class MysqlSchema extends Schema
*/
private function fetchForeignKeyInfo(string $table): array
{
$schema = $this->conn->dsn['database'];
$schema = $this->conn->getDatabase();
$data = $this->fetchQueryData(
<<<END
SELECT CONSTRAINT_NAME AS `key_name`,

View File

@ -193,8 +193,8 @@ class PgsqlSchema extends Schema
*/
public function fetchMetaInfo($table, $infoTable, $orderBy = null)
{
$catalog = $this->conn->dsn['database'];
return $this->fetchQueryData(sprintf(
$catalog = $this->conn->getDatabase();
$info = $this->fetchQueryData(sprintf(
<<<'END'
SELECT * FROM information_schema.%1$s
WHERE table_catalog = '%2$s' AND table_name = '%3$s'%4$s;
@ -204,6 +204,10 @@ class PgsqlSchema extends Schema
$table,
($orderBy ? " ORDER BY {$orderBy}" : '')
));
return array_map(function (array $cols): array {
return array_change_key_case($cols, CASE_LOWER);
}, $info);
}
/**

View File

@ -72,7 +72,7 @@ class Schema
if (is_null($conn)) {
$key = 'default';
} else {
$key = md5(serialize($conn->dsn));
$key = hash('md5', $conn->getDSN('string'));
}
if (is_null($dbtype)) {
@ -343,7 +343,7 @@ class Schema
{
global $_PEAR;
$res = $this->conn->query('DROP TABLE ' . $this->quoteIdentifier($name));
$res = $this->conn->exec('DROP TABLE ' . $this->quoteIdentifier($name));
if ($_PEAR->isError($res)) {
PEAR_ErrorToPEAR_Exception($res);
@ -372,7 +372,7 @@ class Schema
{
global $_PEAR;
$qry = [];
$statements = [];
if (!is_array($columnNames)) {
$columnNames = [$columnNames];
@ -382,14 +382,20 @@ class Schema
$name = "{$table}_" . implode("_", $columnNames) . "_idx";
}
$this->appendCreateIndex($qry, $table, $name, $columnNames);
$this->appendCreateIndex($statements, $table, $name, $columnNames);
$res = $this->conn->query(implode('; ', $qry));
$this->conn->beginTransaction();
if ($_PEAR->isError($res)) {
PEAR_ErrorToPEAR_Exception($res);
foreach ($statements as $sql) {
$res = $this->conn->exec($sql);
if ($_PEAR->isError($res)) {
$this->conn->rollback();
PEAR_ErrorToPEAR_Exception($res);
}
}
$this->conn->commit();
return true;
}
@ -409,12 +415,18 @@ class Schema
$statements = [];
$this->appendDropIndex($statements, $table, $name);
$res = $this->conn->query(implode(";\n", $statements));
$this->conn->beginTransaction();
if ($_PEAR->isError($res)) {
PEAR_ErrorToPEAR_Exception($res);
foreach ($statements as $sql) {
$res = $this->conn->exec($sql);
if ($_PEAR->isError($res)) {
$this->conn->rollback();
PEAR_ErrorToPEAR_Exception($res);
}
}
$this->conn->commit();
return true;
}
@ -435,7 +447,7 @@ class Schema
$sql = 'ALTER TABLE ' . $this->quoteIdentifier($table) .
' ADD COLUMN ' . $this->columnSql($name, $columndef);
$res = $this->conn->query($sql);
$res = $this->conn->exec($sql);
if ($_PEAR->isError($res)) {
PEAR_ErrorToPEAR_Exception($res);
@ -463,7 +475,7 @@ class Schema
$sql = 'ALTER TABLE ' . $this->quoteIdentifier($table) .
' MODIFY COLUMN ' . $this->columnSql($name, $columndef);
$res = $this->conn->query($sql);
$res = $this->conn->exec($sql);
if ($_PEAR->isError($res)) {
PEAR_ErrorToPEAR_Exception($res);
@ -490,7 +502,7 @@ class Schema
$sql = 'ALTER TABLE ' . $this->quoteIdentifier($table) .
' DROP COLUMN ' . $columnName;
$res = $this->conn->query($sql);
$res = $this->conn->exec($sql);
if ($_PEAR->isError($res)) {
PEAR_ErrorToPEAR_Exception($res);
@ -532,19 +544,24 @@ class Schema
{
global $_PEAR;
$ok = true;
$this->conn->beginTransaction();
foreach ($statements as $sql) {
if (defined('DEBUG_INSTALLER')) {
echo "<code>" . htmlspecialchars($sql) . "</code><br/>\n";
}
$res = $this->conn->query($sql);
$res = $this->conn->exec($sql);
if ($_PEAR->isError($res)) {
$this->conn->rollback();
common_debug('PEAR exception on query: ' . $sql);
PEAR_ErrorToPEAR_Exception($res);
}
}
return $ok;
$this->conn->commit();
return true;
}
/**
@ -849,7 +866,11 @@ class Schema
public function quoteValue($val)
{
return $this->conn->quoteSmart($val);
// MDB2_Driver_Common::quote changes empty strings to "NULL".
if ($val === '') {
return '';
}
return $this->conn->quote($val);
}
/**
@ -1190,7 +1211,7 @@ class Schema
* @return array of arrays
* @throws PEAR_Exception
*/
protected function fetchQueryData($sql)
protected function fetchQueryData(string $sql): array
{
global $_PEAR;
@ -1200,8 +1221,7 @@ class Schema
}
$out = [];
$row = [];
while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
while (!empty($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$out[] = $row;
}
$res->free();

View File

@ -72,8 +72,7 @@ $default =
'require_prefix' => 'classes/',
'class_prefix' => '',
'mirror' => null,
'utf8' => true,
'db_driver' => 'DB', # XXX: JanRain libs only work with DB
'db_driver' => 'MDB2',
'disable_null_strings' => true, // 'NULL' can be harmful
'quote_identifiers' => true,
'type' => 'mysql',

View File

@ -95,11 +95,9 @@ global $_PEAR;
$_PEAR = new PEAR;
$_PEAR->setErrorHandling(PEAR_ERROR_CALLBACK, 'PEAR_ErrorToPEAR_Exception');
require_once 'DB.php';
require_once 'MDB2.php';
require_once 'DB/DataObject.php';
require_once 'DB/DataObject/Cast.php'; # for dates
global $_DB;
$_DB = new DB;
require_once 'DB/DataObject/Cast.php'; // for dates
require_once INSTALLDIR . '/lib/util/language.php';
@ -129,13 +127,14 @@ function GNUsocial_class_autoload($cls)
$lib_path = INSTALLDIR . '/lib/';
$lib_dirs = array_map(function ($dir) {
return '/lib/' . $dir . '/';
},
array_filter(scandir($lib_path),
function ($dir) use ($lib_path) {
// Filter out files and both hidden and implicit folders
return $dir[0] != '.' && is_dir($lib_path . $dir);
}));
return '/lib/' . $dir . '/';
}, array_filter(
scandir($lib_path),
function ($dir) use ($lib_path) {
// Filter out files and both hidden and implicit directories.
return $dir[0] !== '.' && is_dir($lib_path . $dir);
}
));
$found = false;
foreach (array_merge(['/classes/'], $lib_dirs) as $dir) {

View File

@ -66,12 +66,12 @@ abstract class Installer
'mysql' => [
'name' => 'MariaDB 10.3+',
'check_module' => 'mysqli',
'scheme' => 'mysqli', // DSN prefix for PEAR::DB
'scheme' => 'mysqli', // DSN prefix for MDB2
],
'pgsql' => [
'name' => 'PostgreSQL 11+',
'check_module' => 'pgsql',
'scheme' => 'pgsql', // DSN prefix for PEAR::DB
'scheme' => 'pgsql', // DSN prefix for MDB2
]
];
@ -283,64 +283,45 @@ abstract class Installer
*/
public function setupDatabase()
{
if ($this->db) {
throw new Exception("Bad order of operations: DB already set up.");
if (!empty($this->db)) {
throw new Exception('Bad order of operations: DB already set up.');
}
$this->updateStatus("Starting installation...");
$this->updateStatus('Starting installation...');
if (empty($this->password)) {
$auth = '';
} else {
$auth = ":$this->password";
$auth = '';
if (!empty($this->password)) {
$auth .= ":{$this->password}";
}
$scheme = self::$dbModules[$this->dbtype]['scheme'];
$dsn = "{$scheme}://{$this->username}{$auth}@{$this->host}/{$this->database}";
$this->updateStatus("Checking database...");
$this->updateStatus('Checking database...');
$conn = $this->connectDatabase($dsn);
if (!$conn instanceof DB_common) {
// Is not the right instance
throw new Exception('Cannot connect to database: ' . $conn->getMessage());
$charset = ($this->dbtype !== 'mysql') ? 'UTF8' : 'utf8mb4';
$server_charset = $this->getDatabaseCharset($conn, $this->dbtype);
// Ensure the database server character set is UTF-8.
$conn->exec("SET NAMES '{$charset}'");
if ($server_charset !== $charset) {
$this->updateStatus(
'GNU social requires the "' . $charset . '" character set. '
. 'Yours is ' . htmlentities($server_charset)
);
return false;
}
switch ($this->dbtype) {
case 'pgsql':
// ensure the timezone is UTC
$conn->query("SET TIME ZONE INTERVAL '+00:00' HOUR TO MINUTE");
// ensure the database encoding is UTF8
$conn->query("SET NAMES 'UTF8'");
$server_encoding = $conn->getRow('SHOW server_encoding')[0];
if ($server_encoding !== 'UTF8') {
$this->updateStatus(
'GNU social requires the UTF8 character encoding. Yours is ' .
htmlentities($server_encoding)
);
return false;
}
break;
case 'mysql':
// ensure the timezone is UTC
$conn->query("SET time_zone = '+0:00'");
// ensure the database encoding is utf8mb4
$conn->query("SET NAMES 'utf8mb4'");
$server_encoding = $conn->getRow("SHOW VARIABLES LIKE 'character_set_server'")[1];
if ($server_encoding !== 'utf8mb4') {
$this->updateStatus(
'GNU social requires the utf8mb4 character encoding. Yours is ' .
htmlentities($server_encoding)
);
return false;
}
break;
default:
$this->updateStatus('Unknown DB type selected: ' . $this->dbtype);
return false;
// Ensure the timezone is UTC.
if ($this->dbtype !== 'mysql') {
$conn->exec("SET TIME ZONE INTERVAL '+00:00' HOUR TO MINUTE");
} else {
$conn->exec("SET time_zone = '+0:00'");
}
$res = $this->updateStatus("Creating database tables...");
$res = $this->updateStatus('Creating database tables...');
if (!$this->createCoreTables($conn)) {
$this->updateStatus("Error creating tables.", true);
$this->updateStatus('Error creating tables.', true);
return false;
}
@ -365,21 +346,67 @@ abstract class Installer
* Open a connection to the database.
*
* @param string $dsn
* @return DB|DB_Error
* @return MDB2_Driver_Common
* @throws Exception
*/
public function connectDatabase(string $dsn)
protected function connectDatabase(string $dsn)
{
global $_DB;
return $_DB->connect($dsn);
$conn = MDB2::connect($dsn);
if (MDB2::isError($conn)) {
throw new Exception(
'Cannot connect to database: ' . $conn->getMessage()
);
}
return $conn;
}
/**
* Get the database server character set.
*
* @param MDB2_Driver_Common $conn
* @param string $dbtype
* @return string
* @throws Exception
*/
protected function getDatabaseCharset($conn, string $dbtype): string
{
$database = $conn->getDatabase();
switch ($dbtype) {
case 'pgsql':
$res = $conn->query('SHOW server_encoding');
if (MDB2::isError($res)) {
throw new Exception($res->getMessage());
}
$ret = $res->fetchOne();
break;
case 'mysql':
$res = $conn->query(
"SHOW SESSION VARIABLES LIKE 'character_set_server'"
);
if (MDB2::isError($res)) {
throw new Exception($res->getMessage());
}
[, $ret] = $res->fetchRow(MDB2_FETCHMODE_ORDERED);
break;
default:
throw new Exception('Unknown DB type selected.');
}
if (MDB2::isError($ret)) {
throw new Exception($ret->getMessage());
}
return $ret;
}
/**
* Create core tables on the given database connection.
*
* @param DB_common $conn
* @param MDB2_Driver_Common $conn
* @return bool
*/
public function createCoreTables(DB_common $conn): bool
public function createCoreTables($conn): bool
{
$schema = Schema::get($conn, $this->dbtype);
$tableDefs = $this->getCoreSchema();
@ -520,11 +547,11 @@ abstract class Installer
* Install schema into the database
*
* @param string $filename location of database schema file
* @param DB_common $conn connection to database
* @param MDB2_Driver_Common $conn connection to database
*
* @return bool - indicating success or failure
*/
public function runDbScript(string $filename, DB_common $conn): bool
public function runDbScript(string $filename, $conn): bool
{
$sql = trim(file_get_contents(INSTALLDIR . '/db/' . $filename));
$stmts = explode(';', $sql);

View File

@ -0,0 +1,204 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* @package GNUsocial
* @author Alexei Sorokin <sor.alexei@meowr.ru>
* @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
defined('GNUSOCIAL') || die();
require_once 'Auth/OpenID/DatabaseConnection.php';
/**
* A DB abstraction error for OpenID's Auth_OpenID_SQLStore
*
* @package GNUsocial
* @author Alexei Sorokin <sor.alexei@meowr.ru>
* @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class SQLStore_DB_Connection extends Auth_OpenID_DatabaseConnection
{
private $conn = null;
private $autocommit = true;
/**
* @param string|array $dsn
* @param array $options
*/
public function __construct($dsn, array $options = [])
{
if (!is_array($dsn)) {
$dsn = MDB2::parseDSN($dsn);
}
$dsn['new_link'] = true;
// To create a new Database connection is an absolute must, because
// php-openid code delays its transactions commitment.
// Is a must because our Internal Session Handler uses the database
// and depends on immediate commitment.
$this->conn = MDB2::connect($dsn, $options);
if (MDB2::isError($this->conn)) {
throw new ServerException($this->conn->getMessage());
}
}
public function __destruct()
{
$this->conn->disconnect();
}
/**
* Sets auto-commit mode on this database connection.
*
* @param bool $mode
*/
public function autoCommit($mode)
{
$this->autocommit = $mode;
if ($mode && $this->conn->inTransaction()) {
$this->commit();
}
}
/**
* Run an SQL query with the specified parameters, if any.
*
* @param string $sql
* @param array $params
* @param bool $is_manip
* @return mixed
*/
private function _query(string $sql, array $params = [], bool $is_manip)
{
$stmt_type = $is_manip ? MDB2_PREPARE_MANIP : MDB2_PREPARE_RESULT;
if ($is_manip && !$this->autocommit) {
$this->begin();
}
$split = preg_split(
'/((?<!\\\)[&?!]|\'!\')/',
$sql,
-1,
PREG_SPLIT_DELIM_CAPTURE
);
$sql = '';
// '!' has no meaning in MDB2, but MDB2 can detect types like BLOB.
$i = 0;
foreach ($split as $part) {
if (!in_array($part, ['?', '!', "'!'"])) {
$sql .= preg_replace('/\\\([!])/', '\\1', $part);
} else {
++$i;
$sql .= '?';
}
}
$stmt = $this->conn->prepare($sql, null, $stmt_type);
if (MDB2::isError($stmt)) {
// php-openid actually expects PEAR_Error.
return $res;
}
if (count($params) > 0) {
$stmt->bindValueArray($params);
}
$res = $stmt->execute();
if (MDB2::isError($res)) {
return $res;
}
return $res;
}
/**
* Run an SQL query with the specified parameters, if any.
*
* @param string $sql
* @param array $params
* @return mixed
*/
public function query($sql, $params = [])
{
return $this->_query($sql, $params, true);
}
public function begin()
{
$this->conn->beginTransaction();
}
public function commit()
{
$this->conn->commit();
}
public function rollback()
{
$this->conn->rollback();
}
/**
* Run an SQL query and return the first column of the first row of the
* result set, if any.
*
* @param string $sql
* @param array $params
* @return string|PEAR_Error
*/
public function getOne($sql, $params = [])
{
$res = $this->_query($sql, $params, false);
if (MDB2::isError($res)) {
return $res;
}
return $res->fetchOne() ?? '';
}
/**
* Run an SQL query and return the first row of the result set, if any.
*
* @param string $sql
* @param array $params
* @return array|PEAR_Error
*/
public function getRow($sql, $params = [])
{
$res = $this->_query($sql, $params, false);
if (MDB2::isError($res)) {
return $res;
}
return $res->fetchRow(MDB2_FETCHMODE_ASSOC);
}
/**
* Run an SQL query with the specified parameters, if any.
*
* @param string $sql
* @param array $params
* @return array|PEAR_Error
*/
public function getAll($sql, $params = [])
{
$res = $this->_query($sql, $params, false);
if (MDB2::isError($res)) {
return $res;
}
return $res->fetchAll(MDB2_FETCHMODE_ASSOC);
}
}

View File

@ -21,11 +21,12 @@
defined('GNUSOCIAL') || die();
require_once('Auth/OpenID.php');
require_once('Auth/OpenID/Consumer.php');
require_once('Auth/OpenID/Server.php');
require_once('Auth/OpenID/SReg.php');
require_once('Auth/OpenID/MySQLStore.php');
require_once __DIR__ . '/lib/openiddbconn.php';
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/Consumer.php';
require_once 'Auth/OpenID/Server.php';
require_once 'Auth/OpenID/SReg.php';
// About one year cookie expiry
@ -34,31 +35,24 @@ define('OPENID_COOKIE_KEY', 'lastusedopenid');
function oid_store()
{
global $_PEAR;
static $store = null;
if (is_null($store)) {
// To create a new Database connection is an absolute must
// because database is in transaction (auto-commit = false)
// mode during OpenID operation
// Is a must because our Internal Session Handler uses database
// and depends on auto-commit = true
$dsn = common_config('db', 'database');
$options = PEAR::getStaticProperty('DB', 'options');
$options = $_PEAR->getStaticProperty('MDB2', 'options');
if (!is_array($options)) {
$options = [];
}
$db = DB::connect($dsn, $options);
if ((new PEAR)->isError($db)) {
throw new ServerException($db->getMessage());
}
$dbconn = new SQLStore_DB_Connection($dsn, $options);
switch (common_config('db', 'type')) {
case 'pgsql':
$store = new Auth_OpenID_PostgreSQLStore($db);
$store = new Auth_OpenID_PostgreSQLStore($dbconn);
break;
case 'mysql':
$store = new Auth_OpenID_MySQLStore($db);
$store = new Auth_OpenID_MySQLStore($dbconn);
break;
default:
throw new ServerException('Unknown DB type selected.');
@ -247,8 +241,12 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
} else {
// Generate form markup and render it.
$form_id = 'openid_message';
$form_html = $auth_request->formMarkup($trust_root, $process_url,
$immediate, array('id' => $form_id));
$form_html = $auth_request->formMarkup(
$trust_root,
$process_url,
$immediate,
['id' => $form_id]
);
// XXX: This is cheap, but things choke if we don't escape ampersands
// in the HTML attributes

View File

@ -121,7 +121,7 @@ function handleError($error)
}
}
if ($error instanceof DB_DataObject_Error
|| $error instanceof DB_Error
|| $error instanceof MDB2_Error
|| ($error instanceof PEAR_Exception && $error->getCode() == -24)
) {
//If we run into a DB error, assume we can't connect to the DB at all