Nouveaux fichiers
This commit is contained in:
parent
177deca5de
commit
edf52c5e1a
@ -186,7 +186,7 @@ class DOLIAuth {
|
||||
$storage_path = "Auth/Container/" . $driver . ".php";
|
||||
$storage_class = "Auth_Container_" . $driver;
|
||||
|
||||
require_once $storage_path;
|
||||
require_once DOL_DOCUMENT_ROOT."/includes/pear/".$storage_path;
|
||||
|
||||
return new $storage_class($options);
|
||||
}
|
||||
|
||||
152
htdocs/includes/pear/Auth/Container.php
Normal file
152
htdocs/includes/pear/Auth/Container.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
//
|
||||
// +----------------------------------------------------------------------+
|
||||
// | PHP Version 4 |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Copyright (c) 1997-2003 The PHP Group |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | This source file is subject to version 2.02 of the PHP license, |
|
||||
// | that is bundled with this package in the file LICENSE, and is |
|
||||
// | available at through the world-wide-web at |
|
||||
// | http://www.php.net/license/2_02.txt. |
|
||||
// | If you did not receive a copy of the PHP license and are unable to |
|
||||
// | obtain it through the world-wide-web, please send a note to |
|
||||
// | license@php.net so we can mail you a copy immediately. |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Authors: Martin Jansen <mj@php.net> |
|
||||
// +----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
|
||||
define("AUTH_METHOD_NOT_SUPPORTED", -4);
|
||||
|
||||
/**
|
||||
* Storage class for fetching login data
|
||||
*
|
||||
* @author Martin Jansen <mj@php.net>
|
||||
* @package Auth
|
||||
*/
|
||||
class Auth_Container
|
||||
{
|
||||
|
||||
/**
|
||||
* User that is currently selected from the storage container.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
var $activeUser = "";
|
||||
|
||||
// {{{ Constructor
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Has to be overwritten by each storage class
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function Auth_Container()
|
||||
{
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ fetchData()
|
||||
|
||||
/**
|
||||
* Fetch data from storage container
|
||||
*
|
||||
* Has to be overwritten by each storage class
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function fetchData()
|
||||
{
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ verifyPassword()
|
||||
|
||||
/**
|
||||
* Crypt and verfiy the entered password
|
||||
*
|
||||
* @param string Entered password
|
||||
* @param string Password from the data container (usually this password
|
||||
* is already encrypted.
|
||||
* @param string Type of algorithm with which the password from
|
||||
* the container has been crypted. (md5, crypt etc.)
|
||||
* Defaults to "md5".
|
||||
* @return bool True, if the passwords match
|
||||
*/
|
||||
function verifyPassword($password1, $password2, $cryptType = "md5")
|
||||
{
|
||||
switch ($cryptType) {
|
||||
case "crypt" :
|
||||
return (($password2 == "**" . $password1) ||
|
||||
(crypt($password1, $password2) == $password2)
|
||||
);
|
||||
break;
|
||||
|
||||
case "none" :
|
||||
return ($password1 == $password2);
|
||||
break;
|
||||
|
||||
case "md5" :
|
||||
return (md5($password1) == $password2);
|
||||
break;
|
||||
|
||||
default :
|
||||
if (function_exists($cryptType)) {
|
||||
return ($cryptType($password1) == $password2);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ listUsers()
|
||||
|
||||
/**
|
||||
* List all users that are available from the storage container
|
||||
*/
|
||||
function listUsers()
|
||||
{
|
||||
return AUTH_METHOD_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ addUser()
|
||||
|
||||
/**
|
||||
* Add a new user to the storage container
|
||||
*
|
||||
* @param string Username
|
||||
* @param string Password
|
||||
* @param array Additional information
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function addUser($username, $password, $additional=null)
|
||||
{
|
||||
return AUTH_METHOD_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ removeUser()
|
||||
|
||||
/**
|
||||
* Remove user from the storage container
|
||||
*
|
||||
* @param string Username
|
||||
*/
|
||||
function removeUser($username)
|
||||
{
|
||||
return AUTH_METHOD_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
}
|
||||
?>
|
||||
394
htdocs/includes/pear/Auth/Container/DB.php
Normal file
394
htdocs/includes/pear/Auth/Container/DB.php
Normal file
@ -0,0 +1,394 @@
|
||||
<?php
|
||||
//
|
||||
// +----------------------------------------------------------------------+
|
||||
// | PHP Version 4 |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | This source file is subject to version 2.02 of the PHP license, |
|
||||
// | that is bundled with this package in the file LICENSE, and is |
|
||||
// | available at through the world-wide-web at |
|
||||
// | http://www.php.net/license/2_02.txt. |
|
||||
// | If you did not receive a copy of the PHP license and are unable to |
|
||||
// | obtain it through the world-wide-web, please send a note to |
|
||||
// | license@php.net so we can mail you a copy immediately. |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Authors: Martin Jansen <mj@php.net> |
|
||||
// +----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
|
||||
//require_once 'Auth/Container.php';
|
||||
require_once DOL_DOCUMENT_ROOT."/includes/pear/Auth/Container.php";
|
||||
//require_once 'DB.php';
|
||||
require_once DOL_DOCUMENT_ROOT."/includes/pear/DB.php";
|
||||
|
||||
/**
|
||||
* Storage driver for fetching login data from a database
|
||||
*
|
||||
* This storage driver can use all databases which are supported
|
||||
* by the PEAR DB abstraction layer to fetch login data.
|
||||
*
|
||||
* @author Martin Jansen <mj@php.net>
|
||||
* @package Auth
|
||||
* @version $Revision$
|
||||
*/
|
||||
class Auth_Container_DB extends Auth_Container
|
||||
{
|
||||
|
||||
/**
|
||||
* Additional options for the storage container
|
||||
* @var array
|
||||
*/
|
||||
var $options = array();
|
||||
|
||||
/**
|
||||
* DB object
|
||||
* @var object
|
||||
*/
|
||||
var $db = null;
|
||||
var $dsn = '';
|
||||
|
||||
/**
|
||||
* User that is currently selected from the DB.
|
||||
* @var string
|
||||
*/
|
||||
var $activeUser = '';
|
||||
|
||||
// {{{ Constructor
|
||||
|
||||
/**
|
||||
* Constructor of the container class
|
||||
*
|
||||
* Initate connection to the database via PEAR::DB
|
||||
*
|
||||
* @param string Connection data or DB object
|
||||
* @return object Returns an error object if something went wrong
|
||||
*/
|
||||
function Auth_Container_DB($dsn)
|
||||
{
|
||||
$this->_setDefaults();
|
||||
|
||||
if (is_array($dsn)) {
|
||||
$this->_parseOptions($dsn);
|
||||
|
||||
if (empty($this->options['dsn'])) {
|
||||
PEAR::raiseError('No connection parameters specified!');
|
||||
}
|
||||
} else {
|
||||
$this->options['dsn'] = $dsn;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _connect()
|
||||
|
||||
/**
|
||||
* Connect to database by using the given DSN string
|
||||
*
|
||||
* @access private
|
||||
* @param string DSN string
|
||||
* @return mixed Object on error, otherwise bool
|
||||
*/
|
||||
function _connect($dsn)
|
||||
{
|
||||
if (is_string($dsn) || is_array($dsn)) {
|
||||
$this->db = DB::Connect($dsn);
|
||||
} elseif (get_parent_class($dsn) == "db_common") {
|
||||
$this->db = $dsn;
|
||||
} elseif (DB::isError($dsn)) {
|
||||
return PEAR::raiseError($dsn->getMessage(), $dsn->getCode());
|
||||
} else {
|
||||
return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__,
|
||||
41,
|
||||
PEAR_ERROR_RETURN,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if (DB::isError($this->db) || PEAR::isError($this->db)) {
|
||||
return PEAR::raiseError($this->db->getMessage(), $this->db->getCode());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _prepare()
|
||||
|
||||
/**
|
||||
* Prepare database connection
|
||||
*
|
||||
* This function checks if we have already opened a connection to
|
||||
* the database. If that's not the case, a new connection is opened.
|
||||
*
|
||||
* @access private
|
||||
* @return mixed True or a DB error object.
|
||||
*/
|
||||
function _prepare()
|
||||
{
|
||||
if (!DB::isConnection($this->db)) {
|
||||
$res = $this->_connect($this->options['dsn']);
|
||||
if(DB::isError($res) || PEAR::isError($res)){
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ query()
|
||||
|
||||
/**
|
||||
* Prepare query to the database
|
||||
*
|
||||
* This function checks if we have already opened a connection to
|
||||
* the database. If that's not the case, a new connection is opened.
|
||||
* After that the query is passed to the database.
|
||||
*
|
||||
* @access public
|
||||
* @param string Query string
|
||||
* @return mixed a DB_result object or DB_OK on success, a DB
|
||||
* or PEAR error on failure
|
||||
*/
|
||||
function query($query)
|
||||
{
|
||||
$err = $this->_prepare();
|
||||
if ($err !== true) {
|
||||
return $err;
|
||||
}
|
||||
return $this->db->query($query);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _setDefaults()
|
||||
|
||||
/**
|
||||
* Set some default options
|
||||
*
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
function _setDefaults()
|
||||
{
|
||||
$this->options['table'] = 'auth';
|
||||
$this->options['usernamecol'] = 'username';
|
||||
$this->options['passwordcol'] = 'password';
|
||||
$this->options['dsn'] = '';
|
||||
$this->options['db_fields'] = '';
|
||||
$this->options['cryptType'] = 'md5';
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _parseOptions()
|
||||
|
||||
/**
|
||||
* Parse options passed to the container class
|
||||
*
|
||||
* @access private
|
||||
* @param array
|
||||
*/
|
||||
function _parseOptions($array)
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
if (isset($this->options[$key])) {
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Include additional fields if they exist */
|
||||
if(!empty($this->options['db_fields'])){
|
||||
if(is_array($this->options['db_fields'])){
|
||||
$this->options['db_fields'] = join($this->options['db_fields'], ', ');
|
||||
}
|
||||
$this->options['db_fields'] = ', '.$this->options['db_fields'];
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ fetchData()
|
||||
|
||||
/**
|
||||
* Get user information from database
|
||||
*
|
||||
* This function uses the given username to fetch
|
||||
* the corresponding login data from the database
|
||||
* table. If an account that matches the passed username
|
||||
* and password is found, the function returns true.
|
||||
* Otherwise it returns false.
|
||||
*
|
||||
* @param string Username
|
||||
* @param string Password
|
||||
* @return mixed Error object or boolean
|
||||
*/
|
||||
function fetchData($username, $password)
|
||||
{
|
||||
// Prepare for a database query
|
||||
$err = $this->_prepare();
|
||||
if ($err !== true) {
|
||||
return PEAR::raiseError($err->getMessage(), $err->getCode());
|
||||
}
|
||||
|
||||
// Find if db_fileds contains a *, i so assume all col are selected
|
||||
if(strstr($this->options['db_fields'], '*')){
|
||||
$sql_from = "*";
|
||||
}
|
||||
else{
|
||||
$sql_from = $this->options['usernamecol'] . ", ".$this->options['passwordcol'].$this->options['db_fields'];
|
||||
}
|
||||
|
||||
$query = "SELECT ! FROM ! WHERE ! = ?";
|
||||
$query_params = array(
|
||||
$sql_from,
|
||||
$this->options['table'],
|
||||
$this->options['usernamecol'],
|
||||
$username
|
||||
);
|
||||
$res = $this->db->getRow($query, $query_params, DB_FETCHMODE_ASSOC);
|
||||
|
||||
if (DB::isError($res)) {
|
||||
return PEAR::raiseError($res->getMessage(), $res->getCode());
|
||||
}
|
||||
if (!is_array($res)) {
|
||||
$this->activeUser = '';
|
||||
return false;
|
||||
}
|
||||
if ($this->verifyPassword(trim($password),
|
||||
trim($res[$this->options['passwordcol']]),
|
||||
$this->options['cryptType'])) {
|
||||
// Store additional field values in the session
|
||||
foreach ($res as $key => $value) {
|
||||
if ($key == $this->options['passwordcol'] ||
|
||||
$key == $this->options['usernamecol']) {
|
||||
continue;
|
||||
}
|
||||
Auth::setAuthData($key, $value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->activeUser = $res[$this->options['usernamecol']];
|
||||
return false;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ listUsers()
|
||||
|
||||
function listUsers()
|
||||
{
|
||||
$err = $this->_prepare();
|
||||
if ($err !== true) {
|
||||
return PEAR::raiseError($err->getMessage(), $err->getCode());
|
||||
}
|
||||
|
||||
$retVal = array();
|
||||
|
||||
// Find if db_fileds contains a *, i so assume all col are selected
|
||||
if(strstr($this->options['db_fields'], '*') || empty($this->options['db_fields'])){
|
||||
$sql_from = "*";
|
||||
}
|
||||
else{
|
||||
$sql_from = $this->options['usernamecol'] . ", ".$this->options['passwordcol'].$this->options['db_fields'];
|
||||
}
|
||||
|
||||
$query = sprintf("SELECT %s FROM %s",
|
||||
$sql_from,
|
||||
$this->options['table']
|
||||
);
|
||||
$res = $this->db->getAll($query, null, DB_FETCHMODE_ASSOC);
|
||||
|
||||
if (DB::isError($res)) {
|
||||
return PEAR::raiseError($res->getMessage(), $res->getCode());
|
||||
} else {
|
||||
foreach ($res as $user) {
|
||||
$user['username'] = $user[$this->options['usernamecol']];
|
||||
$retVal[] = $user;
|
||||
}
|
||||
}
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ addUser()
|
||||
|
||||
/**
|
||||
* Add user to the storage container
|
||||
*
|
||||
* @access public
|
||||
* @param string Username
|
||||
* @param string Password
|
||||
* @param mixed Additional information that are stored in the DB
|
||||
*
|
||||
* @return mixed True on success, otherwise error object
|
||||
*/
|
||||
function addUser($username, $password, $additional = "")
|
||||
{
|
||||
if (function_exists($this->options['cryptType'])) {
|
||||
$cryptFunction = $this->options['cryptType'];
|
||||
} else {
|
||||
$cryptFunction = 'md5';
|
||||
}
|
||||
|
||||
$additional_key = '';
|
||||
$additional_value = '';
|
||||
|
||||
if (is_array($additional)) {
|
||||
foreach ($additional as $key => $value) {
|
||||
$additional_key .= ', ' . $key;
|
||||
$additional_value .= ", '" . $value . "'";
|
||||
}
|
||||
}
|
||||
|
||||
$query = sprintf("INSERT INTO %s (%s, %s%s) VALUES ('%s', '%s'%s)",
|
||||
$this->options['table'],
|
||||
$this->options['usernamecol'],
|
||||
$this->options['passwordcol'],
|
||||
$additional_key,
|
||||
$username,
|
||||
$cryptFunction($password),
|
||||
$additional_value
|
||||
);
|
||||
|
||||
$res = $this->query($query);
|
||||
|
||||
if (DB::isError($res)) {
|
||||
return PEAR::raiseError($res->getMessage(), $res->getCode());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ removeUser()
|
||||
|
||||
/**
|
||||
* Remove user from the storage container
|
||||
*
|
||||
* @access public
|
||||
* @param string Username
|
||||
*
|
||||
* @return mixed True on success, otherwise error object
|
||||
*/
|
||||
function removeUser($username)
|
||||
{
|
||||
$query = sprintf("DELETE FROM %s WHERE %s = '%s'",
|
||||
$this->options['table'],
|
||||
$this->options['usernamecol'],
|
||||
$username
|
||||
);
|
||||
|
||||
$res = $this->query($query);
|
||||
|
||||
if (DB::isError($res)) {
|
||||
return PEAR::raiseError($res->getMessage(), $res->getCode());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
?>
|
||||
948
htdocs/includes/pear/DB.php
Normal file
948
htdocs/includes/pear/DB.php
Normal file
@ -0,0 +1,948 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
|
||||
// +----------------------------------------------------------------------+
|
||||
// | PHP Version 4 |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Copyright (c) 1997-2003 The PHP Group |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | This source file is subject to version 2.02 of the PHP license, |
|
||||
// | that is bundled with this package in the file LICENSE, and is |
|
||||
// | available at through the world-wide-web at |
|
||||
// | http://www.php.net/license/2_02.txt. |
|
||||
// | If you did not receive a copy of the PHP license and are unable to |
|
||||
// | obtain it through the world-wide-web, please send a note to |
|
||||
// | license@php.net so we can mail you a copy immediately. |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Authors: Stig Bakken <ssb@php.net> |
|
||||
// | Tomas V.V.Cox <cox@idecnet.com> |
|
||||
// +----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Database independent query interface.
|
||||
//
|
||||
|
||||
//require_once "PEAR.php";
|
||||
require_once DOL_DOCUMENT_ROOT."/includes/pear/PEAR.php";
|
||||
// {{{ constants
|
||||
// {{{ error codes
|
||||
/*
|
||||
* The method mapErrorCode in each DB_dbtype implementation maps
|
||||
* native error codes to one of these.
|
||||
*
|
||||
* If you add an error code here, make sure you also add a textual
|
||||
* version of it in DB::errorMessage().
|
||||
*/
|
||||
|
||||
define("DB_OK", 1);
|
||||
define("DB_ERROR", -1);
|
||||
define("DB_ERROR_SYNTAX", -2);
|
||||
define("DB_ERROR_CONSTRAINT", -3);
|
||||
define("DB_ERROR_NOT_FOUND", -4);
|
||||
define("DB_ERROR_ALREADY_EXISTS", -5);
|
||||
define("DB_ERROR_UNSUPPORTED", -6);
|
||||
define("DB_ERROR_MISMATCH", -7);
|
||||
define("DB_ERROR_INVALID", -8);
|
||||
define("DB_ERROR_NOT_CAPABLE", -9);
|
||||
define("DB_ERROR_TRUNCATED", -10);
|
||||
define("DB_ERROR_INVALID_NUMBER", -11);
|
||||
define("DB_ERROR_INVALID_DATE", -12);
|
||||
define("DB_ERROR_DIVZERO", -13);
|
||||
define("DB_ERROR_NODBSELECTED", -14);
|
||||
define("DB_ERROR_CANNOT_CREATE", -15);
|
||||
define("DB_ERROR_CANNOT_DELETE", -16);
|
||||
define("DB_ERROR_CANNOT_DROP", -17);
|
||||
define("DB_ERROR_NOSUCHTABLE", -18);
|
||||
define("DB_ERROR_NOSUCHFIELD", -19);
|
||||
define("DB_ERROR_NEED_MORE_DATA", -20);
|
||||
define("DB_ERROR_NOT_LOCKED", -21);
|
||||
define("DB_ERROR_VALUE_COUNT_ON_ROW", -22);
|
||||
define("DB_ERROR_INVALID_DSN", -23);
|
||||
define("DB_ERROR_CONNECT_FAILED", -24);
|
||||
define("DB_ERROR_EXTENSION_NOT_FOUND",-25);
|
||||
define("DB_ERROR_ACCESS_VIOLATION", -26);
|
||||
define("DB_ERROR_NOSUCHDB", -27);
|
||||
|
||||
// }}}
|
||||
// {{{ warning codes
|
||||
/*
|
||||
* Warnings are not detected as errors by DB::isError(), and are not
|
||||
* fatal. You can detect whether an error is in fact a warning with
|
||||
* DB::isWarning().
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
define('DB_WARNING', -1000);
|
||||
define('DB_WARNING_READ_ONLY', -1001);
|
||||
|
||||
// }}}
|
||||
// {{{ prepared statement-related
|
||||
/*
|
||||
* These constants are used when storing information about prepared
|
||||
* statements (using the "prepare" method in DB_dbtype).
|
||||
*
|
||||
* The prepare/execute model in DB is mostly borrowed from the ODBC
|
||||
* extension, in a query the "?" character means a scalar parameter.
|
||||
* There are two extensions though, a "&" character means an opaque
|
||||
* parameter. An opaque parameter is simply a file name, the real
|
||||
* data are in that file (useful for putting uploaded files into your
|
||||
* database and such). The "!" char means a parameter that must be
|
||||
* left as it is.
|
||||
* They modify the quote behavoir:
|
||||
* DB_PARAM_SCALAR (?) => 'original string quoted'
|
||||
* DB_PARAM_OPAQUE (&) => 'string from file quoted'
|
||||
* DB_PARAM_MISC (!) => original string
|
||||
*/
|
||||
|
||||
define('DB_PARAM_SCALAR', 1);
|
||||
define('DB_PARAM_OPAQUE', 2);
|
||||
define('DB_PARAM_MISC', 3);
|
||||
|
||||
// }}}
|
||||
// {{{ binary data-related
|
||||
/*
|
||||
* These constants define different ways of returning binary data
|
||||
* from queries. Again, this model has been borrowed from the ODBC
|
||||
* extension.
|
||||
*
|
||||
* DB_BINMODE_PASSTHRU sends the data directly through to the browser
|
||||
* when data is fetched from the database.
|
||||
* DB_BINMODE_RETURN lets you return data as usual.
|
||||
* DB_BINMODE_CONVERT returns data as well, only it is converted to
|
||||
* hex format, for example the string "123" would become "313233".
|
||||
*/
|
||||
|
||||
define('DB_BINMODE_PASSTHRU', 1);
|
||||
define('DB_BINMODE_RETURN', 2);
|
||||
define('DB_BINMODE_CONVERT', 3);
|
||||
|
||||
// }}}
|
||||
// {{{ fetch modes
|
||||
/**
|
||||
* This is a special constant that tells DB the user hasn't specified
|
||||
* any particular get mode, so the default should be used.
|
||||
*/
|
||||
|
||||
define('DB_FETCHMODE_DEFAULT', 0);
|
||||
|
||||
/**
|
||||
* Column data indexed by numbers, ordered from 0 and up
|
||||
*/
|
||||
|
||||
define('DB_FETCHMODE_ORDERED', 1);
|
||||
|
||||
/**
|
||||
* Column data indexed by column names
|
||||
*/
|
||||
|
||||
define('DB_FETCHMODE_ASSOC', 2);
|
||||
|
||||
/**
|
||||
* Column data as object properties
|
||||
*/
|
||||
|
||||
define('DB_FETCHMODE_OBJECT', 3);
|
||||
|
||||
/**
|
||||
* For multi-dimensional results: normally the first level of arrays
|
||||
* is the row number, and the second level indexed by column number or name.
|
||||
* DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays
|
||||
* is the column name, and the second level the row number.
|
||||
*/
|
||||
|
||||
define('DB_FETCHMODE_FLIPPED', 4);
|
||||
|
||||
/* for compatibility */
|
||||
|
||||
define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
|
||||
define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC);
|
||||
define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
|
||||
|
||||
// }}}
|
||||
// {{{ tableInfo() && autoPrepare()-related
|
||||
/**
|
||||
* 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 DB_TABLEINFO_FULL accordingly
|
||||
*/
|
||||
|
||||
define('DB_TABLEINFO_ORDER', 1);
|
||||
define('DB_TABLEINFO_ORDERTABLE', 2);
|
||||
define('DB_TABLEINFO_FULL', 3);
|
||||
|
||||
/*
|
||||
* Used by autoPrepare()
|
||||
*/
|
||||
define('DB_AUTOQUERY_INSERT', 1);
|
||||
define('DB_AUTOQUERY_UPDATE', 2);
|
||||
|
||||
// }}}
|
||||
// }}}
|
||||
|
||||
// {{{ class DB
|
||||
/**
|
||||
* The main "DB" class is simply a container class with some static
|
||||
* methods for creating DB objects as well as some utility functions
|
||||
* common to all parts of DB.
|
||||
*
|
||||
* The object model of DB is as follows (indentation means inheritance):
|
||||
*
|
||||
* DB The main DB class. This is simply a utility class
|
||||
* with some "static" methods for creating DB objects as
|
||||
* well as common utility functions for other DB classes.
|
||||
*
|
||||
* DB_common The base for each DB implementation. Provides default
|
||||
* | implementations (in OO lingo virtual methods) for
|
||||
* | the actual DB implementations as well as a bunch of
|
||||
* | query utility functions.
|
||||
* |
|
||||
* +-DB_mysql The DB implementation for MySQL. Inherits DB_common.
|
||||
* When calling DB::factory or DB::connect for MySQL
|
||||
* connections, the object returned is an instance of this
|
||||
* class.
|
||||
*
|
||||
* @package DB
|
||||
* @author Stig Bakken <ssb@php.net>
|
||||
* @since PHP 4.0
|
||||
*/
|
||||
|
||||
class DB
|
||||
{
|
||||
// {{{ &factory()
|
||||
/**
|
||||
* Create a new DB connection object for the specified database
|
||||
* type
|
||||
*
|
||||
* @param string $type database type, for example "mysql"
|
||||
*
|
||||
* @return mixed a newly created DB object, or a DB error code on
|
||||
* error
|
||||
*
|
||||
* access public
|
||||
*/
|
||||
|
||||
function &factory($type)
|
||||
{
|
||||
@include_once(DOL_DOCUMENT_ROOT."/includes/pear/DB/${type}.php");
|
||||
|
||||
$classname = "DB_${type}";
|
||||
|
||||
if (!class_exists($classname)) {
|
||||
return PEAR::raiseError(null, DB_ERROR_NOT_FOUND,
|
||||
null, null, null, 'DB_Error', true);
|
||||
}
|
||||
|
||||
@$obj =& new $classname;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ &connect()
|
||||
/**
|
||||
* Create a new DB connection object and connect to the specified
|
||||
* database
|
||||
*
|
||||
* @param mixed $dsn "data source name", see the DB::parseDSN
|
||||
* method for a description of the dsn format. Can also be
|
||||
* specified as an array of the format returned by DB::parseDSN.
|
||||
*
|
||||
* @param mixed $options An associative array of option names and
|
||||
* their values. For backwards compatibility, this parameter may
|
||||
* also be a boolean that tells whether the connection should be
|
||||
* persistent. See DB_common::setOption for more information on
|
||||
* connection options.
|
||||
*
|
||||
* @return mixed a newly created DB connection object, or a DB
|
||||
* error object on error
|
||||
*
|
||||
* @see DB::parseDSN
|
||||
* @see DB::isError
|
||||
* @see DB_common::setOption
|
||||
*/
|
||||
function &connect($dsn, $options = false)
|
||||
{
|
||||
if (is_array($dsn)) {
|
||||
$dsninfo = $dsn;
|
||||
} else {
|
||||
$dsninfo = DB::parseDSN($dsn);
|
||||
}
|
||||
$type = $dsninfo["phptype"];
|
||||
|
||||
if (is_array($options) && isset($options["debug"]) &&
|
||||
$options["debug"] >= 2) {
|
||||
// expose php errors with sufficient debug level
|
||||
include_once DOL_DOCUMENT_ROOT."/includes/pear/DB/${type}.php";
|
||||
} else {
|
||||
@include_once DOL_DOCUMENT_ROOT."/includes/pear/DB/${type}.php";
|
||||
}
|
||||
|
||||
$classname = "DB_${type}";
|
||||
if (!class_exists($classname)) {
|
||||
return PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
|
||||
"Unable to include the DB/{$type}.php file for `$dsn'",
|
||||
'DB_Error', true);
|
||||
}
|
||||
|
||||
@$obj =& new $classname;
|
||||
|
||||
if (is_array($options)) {
|
||||
foreach ($options as $option => $value) {
|
||||
$test = $obj->setOption($option, $value);
|
||||
if (DB::isError($test)) {
|
||||
return $test;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$obj->setOption('persistent', $options);
|
||||
}
|
||||
$err = $obj->connect($dsninfo, $obj->getOption('persistent'));
|
||||
if (DB::isError($err)) {
|
||||
$err->addUserInfo($dsn);
|
||||
return $err;
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ apiVersion()
|
||||
/**
|
||||
* Return the DB API version
|
||||
*
|
||||
* @return int the DB API version number
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function apiVersion()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isError()
|
||||
/**
|
||||
* Tell whether a result code from a DB method is an error
|
||||
*
|
||||
* @param int $value result code
|
||||
*
|
||||
* @return bool whether $value is an error
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function isError($value)
|
||||
{
|
||||
return (is_object($value) &&
|
||||
(get_class($value) == 'db_error' ||
|
||||
is_subclass_of($value, 'db_error')));
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isConnection()
|
||||
/**
|
||||
* Tell whether a value is a DB connection
|
||||
*
|
||||
* @param mixed $value value to test
|
||||
*
|
||||
* @return bool whether $value is a DB connection
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function isConnection($value)
|
||||
{
|
||||
return (is_object($value) &&
|
||||
is_subclass_of($value, 'db_common') &&
|
||||
method_exists($value, 'simpleQuery'));
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isManip()
|
||||
/**
|
||||
* Tell whether a query is a data manipulation query (insert,
|
||||
* update or delete) or a data definition query (create, drop,
|
||||
* alter, grant, revoke).
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param string $query the query
|
||||
*
|
||||
* @return boolean whether $query is a data manipulation query
|
||||
*/
|
||||
function isManip($query)
|
||||
{
|
||||
$manips = 'INSERT|UPDATE|DELETE|'.'REPLACE|CREATE|DROP|'.
|
||||
'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK';
|
||||
if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ errorMessage()
|
||||
/**
|
||||
* Return a textual error message for a DB error code
|
||||
*
|
||||
* @param integer $value error code
|
||||
*
|
||||
* @return string error message, or false if the error code was
|
||||
* not recognized
|
||||
*/
|
||||
function errorMessage($value)
|
||||
{
|
||||
static $errorMessages;
|
||||
if (!isset($errorMessages)) {
|
||||
$errorMessages = array(
|
||||
DB_ERROR => 'unknown error',
|
||||
DB_ERROR_ALREADY_EXISTS => 'already exists',
|
||||
DB_ERROR_CANNOT_CREATE => 'can not create',
|
||||
DB_ERROR_CANNOT_DELETE => 'can not delete',
|
||||
DB_ERROR_CANNOT_DROP => 'can not drop',
|
||||
DB_ERROR_CONSTRAINT => 'constraint violation',
|
||||
DB_ERROR_DIVZERO => 'division by zero',
|
||||
DB_ERROR_INVALID => 'invalid',
|
||||
DB_ERROR_INVALID_DATE => 'invalid date or time',
|
||||
DB_ERROR_INVALID_NUMBER => 'invalid number',
|
||||
DB_ERROR_MISMATCH => 'mismatch',
|
||||
DB_ERROR_NODBSELECTED => 'no database selected',
|
||||
DB_ERROR_NOSUCHFIELD => 'no such field',
|
||||
DB_ERROR_NOSUCHTABLE => 'no such table',
|
||||
DB_ERROR_NOT_CAPABLE => 'DB backend not capable',
|
||||
DB_ERROR_NOT_FOUND => 'not found',
|
||||
DB_ERROR_NOT_LOCKED => 'not locked',
|
||||
DB_ERROR_SYNTAX => 'syntax error',
|
||||
DB_ERROR_UNSUPPORTED => 'not supported',
|
||||
DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
|
||||
DB_ERROR_INVALID_DSN => 'invalid DSN',
|
||||
DB_ERROR_CONNECT_FAILED => 'connect failed',
|
||||
DB_OK => 'no error',
|
||||
DB_WARNING => 'unknown warning',
|
||||
DB_WARNING_READ_ONLY => 'read only',
|
||||
DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied',
|
||||
DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
|
||||
DB_ERROR_NOSUCHDB => 'no such database',
|
||||
DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions',
|
||||
DB_ERROR_TRUNCATED => 'truncated'
|
||||
);
|
||||
}
|
||||
|
||||
if (DB::isError($value)) {
|
||||
$value = $value->getCode();
|
||||
}
|
||||
|
||||
return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR];
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ parseDSN()
|
||||
/**
|
||||
* Parse a data source name
|
||||
*
|
||||
* A array with the following keys will be returned:
|
||||
* phptype: Database backend used in PHP (mysql, odbc etc.)
|
||||
* dbsyntax: Database used with regards to SQL syntax etc.
|
||||
* protocol: Communication protocol to use (tcp, unix etc.)
|
||||
* hostspec: Host specification (hostname[:port])
|
||||
* database: Database to use on the DBMS server
|
||||
* username: User name for login
|
||||
* password: Password for login
|
||||
*
|
||||
* The format of the supplied DSN is in its fullest form:
|
||||
*
|
||||
* phptype(dbsyntax)://username:password@protocol+hostspec/database
|
||||
*
|
||||
* Most variations are allowed:
|
||||
*
|
||||
* phptype://username:password@protocol+hostspec:110//usr/db_file.db
|
||||
* phptype://username:password@hostspec/database_name
|
||||
* phptype://username:password@hostspec
|
||||
* phptype://username@hostspec
|
||||
* phptype://hostspec/database
|
||||
* phptype://hostspec
|
||||
* phptype(dbsyntax)
|
||||
* phptype
|
||||
*
|
||||
* @param string $dsn Data Source Name to be parsed
|
||||
*
|
||||
* @return array an associative array
|
||||
*
|
||||
* @author Tomas V.V.Cox <cox@idecnet.com>
|
||||
*/
|
||||
function parseDSN($dsn)
|
||||
{
|
||||
if (is_array($dsn)) {
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
$parsed = array(
|
||||
'phptype' => false,
|
||||
'dbsyntax' => false,
|
||||
'username' => false,
|
||||
'password' => false,
|
||||
'protocol' => false,
|
||||
'hostspec' => false,
|
||||
'port' => false,
|
||||
'socket' => false,
|
||||
'database' => false
|
||||
);
|
||||
|
||||
// Find phptype and dbsyntax
|
||||
if (($pos = strpos($dsn, '://')) !== false) {
|
||||
$str = substr($dsn, 0, $pos);
|
||||
$dsn = substr($dsn, $pos + 3);
|
||||
} else {
|
||||
$str = $dsn;
|
||||
$dsn = NULL;
|
||||
}
|
||||
|
||||
// Get phptype and dbsyntax
|
||||
// $str => phptype(dbsyntax)
|
||||
if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
|
||||
$parsed['phptype'] = $arr[1];
|
||||
$parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2];
|
||||
} else {
|
||||
$parsed['phptype'] = $str;
|
||||
$parsed['dbsyntax'] = $str;
|
||||
}
|
||||
|
||||
if (empty($dsn)) {
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
// Get (if found): username and password
|
||||
// $dsn => username:password@protocol+hostspec/database
|
||||
if (($at = strrpos($dsn,'@')) !== false) {
|
||||
$str = substr($dsn, 0, $at);
|
||||
$dsn = substr($dsn, $at + 1);
|
||||
if (($pos = strpos($str, ':')) !== false) {
|
||||
$parsed['username'] = rawurldecode(substr($str, 0, $pos));
|
||||
$parsed['password'] = rawurldecode(substr($str, $pos + 1));
|
||||
} else {
|
||||
$parsed['username'] = rawurldecode($str);
|
||||
}
|
||||
}
|
||||
|
||||
// Find protocol and hostspec
|
||||
|
||||
// $dsn => proto(proto_opts)/database
|
||||
if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
|
||||
$proto = $match[1];
|
||||
$proto_opts = (!empty($match[2])) ? $match[2] : false;
|
||||
$dsn = $match[3];
|
||||
|
||||
// $dsn => protocol+hostspec/database (old format)
|
||||
} else {
|
||||
if (strpos($dsn, '+') !== false) {
|
||||
list($proto, $dsn) = explode('+', $dsn, 2);
|
||||
}
|
||||
if (strpos($dsn, '/') !== false) {
|
||||
list($proto_opts, $dsn) = explode('/', $dsn, 2);
|
||||
} else {
|
||||
$proto_opts = $dsn;
|
||||
$dsn = null;
|
||||
}
|
||||
}
|
||||
|
||||
// process the different protocol options
|
||||
$parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
|
||||
$proto_opts = rawurldecode($proto_opts);
|
||||
if ($parsed['protocol'] == 'tcp') {
|
||||
if (strpos($proto_opts, ':') !== false) {
|
||||
list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
|
||||
} else {
|
||||
$parsed['hostspec'] = $proto_opts;
|
||||
}
|
||||
} elseif ($parsed['protocol'] == 'unix') {
|
||||
$parsed['socket'] = $proto_opts;
|
||||
}
|
||||
|
||||
// Get dabase if any
|
||||
// $dsn => database
|
||||
if (!empty($dsn)) {
|
||||
// /database
|
||||
if (($pos = strpos($dsn, '?')) === false) {
|
||||
$parsed['database'] = $dsn;
|
||||
// /database?param1=value1¶m2=value2
|
||||
} else {
|
||||
$parsed['database'] = substr($dsn, 0, $pos);
|
||||
$dsn = substr($dsn, $pos + 1);
|
||||
if (strpos($dsn, '&') !== false) {
|
||||
$opts = explode('&', $dsn);
|
||||
} else { // database?param1=value1
|
||||
$opts = array($dsn);
|
||||
}
|
||||
foreach ($opts as $opt) {
|
||||
list($key, $value) = explode('=', $opt);
|
||||
if (!isset($parsed[$key])) { // don't allow params overwrite
|
||||
$parsed[$key] = rawurldecode($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ assertExtension()
|
||||
/**
|
||||
* Load a PHP database extension if it is not loaded already.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param string $name the base name of the extension (without the .so or
|
||||
* .dll suffix)
|
||||
*
|
||||
* @return boolean true if the extension was already or successfully
|
||||
* loaded, false if it could not be loaded
|
||||
*/
|
||||
function assertExtension($name)
|
||||
{
|
||||
if (!extension_loaded($name)) {
|
||||
$dlext = OS_WINDOWS ? '.dll' : '.so';
|
||||
$dlprefix = OS_WINDOWS ? 'php_' : '';
|
||||
@dl($dlprefix . $name . $dlext);
|
||||
return extension_loaded($name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
// }}}
|
||||
|
||||
// {{{ class DB_Error
|
||||
/**
|
||||
* DB_Error implements a class for reporting portable database error
|
||||
* messages.
|
||||
*
|
||||
* @package DB
|
||||
* @author Stig Bakken <ssb@php.net>
|
||||
*/
|
||||
class DB_Error extends DOLIPEAR_Error
|
||||
{
|
||||
// {{{ constructor
|
||||
/**
|
||||
* DB_Error constructor.
|
||||
*
|
||||
* @param mixed $code DB error code, or string with error message.
|
||||
* @param integer $mode what "error mode" to operate in
|
||||
* @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER
|
||||
* @param mixed $debuginfo additional debug info, such as the last query
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @see PEAR_Error
|
||||
*/
|
||||
|
||||
function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
|
||||
$level = E_USER_NOTICE, $debuginfo = null)
|
||||
{
|
||||
if (is_int($code)) {
|
||||
$this->DOLIPEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo);
|
||||
} else {
|
||||
$this->DOLIPEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo);
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
// }}}
|
||||
|
||||
// {{{ class DB_Result
|
||||
/**
|
||||
* This class implements a wrapper for a DB result set.
|
||||
* A new instance of this class will be returned by the DB implementation
|
||||
* after processing a query that returns data.
|
||||
*
|
||||
* @package DB
|
||||
* @author Stig Bakken <ssb@php.net>
|
||||
*/
|
||||
|
||||
class DB_result
|
||||
{
|
||||
// {{{ properties
|
||||
|
||||
var $dbh;
|
||||
var $result;
|
||||
var $row_counter = null;
|
||||
/**
|
||||
* for limit queries, the row to start fetching
|
||||
* @var integer
|
||||
*/
|
||||
var $limit_from = null;
|
||||
|
||||
/**
|
||||
* for limit queries, the number of rows to fetch
|
||||
* @var integer
|
||||
*/
|
||||
var $limit_count = null;
|
||||
|
||||
// }}}
|
||||
// {{{ constructor
|
||||
/**
|
||||
* DB_result constructor.
|
||||
* @param resource &$dbh DB object reference
|
||||
* @param resource $result result resource id
|
||||
* @param array $options assoc array with optional result options
|
||||
*/
|
||||
|
||||
function DB_result(&$dbh, $result, $options = array())
|
||||
{
|
||||
$this->dbh = &$dbh;
|
||||
$this->result = $result;
|
||||
foreach ($options as $key => $value) {
|
||||
$this->setOption($key, $value);
|
||||
}
|
||||
$this->limit_type = $dbh->features['limit'];
|
||||
$this->autofree = $dbh->options['autofree'];
|
||||
$this->fetchmode = $dbh->fetchmode;
|
||||
$this->fetchmode_object_class = $dbh->fetchmode_object_class;
|
||||
}
|
||||
|
||||
function setOption($key, $value = null)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'limit_from':
|
||||
$this->limit_from = $value; break;
|
||||
case 'limit_count';
|
||||
$this->limit_count = $value; break;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ fetchRow()
|
||||
/**
|
||||
* Fetch and return a row of data (it uses driver->fetchInto for that)
|
||||
* @param int $fetchmode format of fetched row
|
||||
* @param int $rownum the row number to fetch
|
||||
*
|
||||
* @return array a row of data, NULL on no more rows or PEAR_Error on error
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
|
||||
{
|
||||
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
|
||||
$fetchmode = $this->fetchmode;
|
||||
}
|
||||
if ($fetchmode === DB_FETCHMODE_OBJECT) {
|
||||
$fetchmode = DB_FETCHMODE_ASSOC;
|
||||
$object_class = $this->fetchmode_object_class;
|
||||
}
|
||||
if ($this->limit_from !== null) {
|
||||
if ($this->row_counter === null) {
|
||||
$this->row_counter = $this->limit_from;
|
||||
// Skip rows
|
||||
if ($this->limit_type == false) {
|
||||
$i = 0;
|
||||
while ($i++ < $this->limit_from) {
|
||||
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->row_counter >= (
|
||||
$this->limit_from + $this->limit_count))
|
||||
{
|
||||
if ($this->autofree) {
|
||||
$this->free();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if ($this->limit_type == 'emulate') {
|
||||
$rownum = $this->row_counter;
|
||||
}
|
||||
$this->row_counter++;
|
||||
}
|
||||
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
|
||||
if ($res === DB_OK) {
|
||||
if (isset($object_class)) {
|
||||
// default mode specified in DB_common::fetchmode_object_class property
|
||||
if ($object_class == 'stdClass') {
|
||||
$arr = (object) $arr;
|
||||
} else {
|
||||
$arr = &new $object_class($arr);
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
if ($res == null && $this->autofree) {
|
||||
$this->free();
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ fetchInto()
|
||||
/**
|
||||
* Fetch a row of data into an existing variable.
|
||||
*
|
||||
* @param mixed &$arr reference to data containing the row
|
||||
* @param integer $fetchmod format of fetched row
|
||||
* @param integer $rownum the row number to fetch
|
||||
*
|
||||
* @return mixed DB_OK on success, NULL on no more rows or
|
||||
* a DB_Error object on error
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
|
||||
{
|
||||
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
|
||||
$fetchmode = $this->fetchmode;
|
||||
}
|
||||
if ($fetchmode === DB_FETCHMODE_OBJECT) {
|
||||
$fetchmode = DB_FETCHMODE_ASSOC;
|
||||
$object_class = $this->fetchmode_object_class;
|
||||
}
|
||||
if ($this->limit_from !== null) {
|
||||
if ($this->row_counter === null) {
|
||||
$this->row_counter = $this->limit_from;
|
||||
// Skip rows
|
||||
if ($this->limit_type == false) {
|
||||
$i = 0;
|
||||
while ($i++ < $this->limit_from) {
|
||||
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->row_counter >= (
|
||||
$this->limit_from + $this->limit_count))
|
||||
{
|
||||
if ($this->autofree) {
|
||||
$this->free();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if ($this->limit_type == 'emulate') {
|
||||
$rownum = $this->row_counter;
|
||||
}
|
||||
|
||||
$this->row_counter++;
|
||||
}
|
||||
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
|
||||
if ($res === DB_OK) {
|
||||
if (isset($object_class)) {
|
||||
// default mode specified in DB_common::fetchmode_object_class property
|
||||
if ($object_class == 'stdClass') {
|
||||
$arr = (object) $arr;
|
||||
} else {
|
||||
$arr = new $object_class($arr);
|
||||
}
|
||||
}
|
||||
return DB_OK;
|
||||
}
|
||||
if ($res == null && $this->autofree) {
|
||||
$this->free();
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ numCols()
|
||||
/**
|
||||
* Get the the number of columns in a result set.
|
||||
*
|
||||
* @return int the number of columns, or a DB error
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function numCols()
|
||||
{
|
||||
return $this->dbh->numCols($this->result);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ numRows()
|
||||
/**
|
||||
* Get the number of rows in a result set.
|
||||
*
|
||||
* @return int the number of rows, or a DB error
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function numRows()
|
||||
{
|
||||
return $this->dbh->numRows($this->result);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ nextResult()
|
||||
/**
|
||||
* Get the next result if a batch of queries was executed.
|
||||
*
|
||||
* @return bool true if a new result is available or false if not.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function nextResult()
|
||||
{
|
||||
return $this->dbh->nextResult($this->result);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ free()
|
||||
/**
|
||||
* Frees the resources allocated for this result set.
|
||||
* @return int error code
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function free()
|
||||
{
|
||||
$err = $this->dbh->freeResult($this->result);
|
||||
if(DB::isError($err)) {
|
||||
return $err;
|
||||
}
|
||||
$this->result = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ tableInfo()
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
function tableInfo($mode = null)
|
||||
{
|
||||
return $this->dbh->tableInfo($this->result, $mode);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getRowCounter()
|
||||
/**
|
||||
* returns the actual row number
|
||||
* @return integer
|
||||
*/
|
||||
function getRowCounter()
|
||||
{
|
||||
return $this->row_counter;
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
// }}}
|
||||
|
||||
// {{{ class DB_Row
|
||||
/**
|
||||
* Pear DB Row Object
|
||||
* @see DB_common::setFetchMode()
|
||||
*/
|
||||
class DB_row
|
||||
{
|
||||
// {{{ constructor
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
* @param resource row data as array
|
||||
*/
|
||||
function DB_row(&$arr)
|
||||
{
|
||||
for (reset($arr); $key = key($arr); next($arr)) {
|
||||
$this->$key = &$arr[$key];
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
// }}}
|
||||
|
||||
?>
|
||||
1398
htdocs/includes/pear/DB/common.php
Normal file
1398
htdocs/includes/pear/DB/common.php
Normal file
File diff suppressed because it is too large
Load Diff
843
htdocs/includes/pear/DB/mysql.php
Normal file
843
htdocs/includes/pear/DB/mysql.php
Normal file
@ -0,0 +1,843 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
|
||||
// +----------------------------------------------------------------------+
|
||||
// | PHP Version 4 |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Copyright (c) 1997-2003 The PHP Group |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | This source file is subject to version 2.02 of the PHP license, |
|
||||
// | that is bundled with this package in the file LICENSE, and is |
|
||||
// | available at through the world-wide-web at |
|
||||
// | http://www.php.net/license/2_02.txt. |
|
||||
// | If you did not receive a copy of the PHP license and are unable to |
|
||||
// | obtain it through the world-wide-web, please send a note to |
|
||||
// | license@php.net so we can mail you a copy immediately. |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Author: Stig Bakken <ssb@php.net> |
|
||||
// +----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Database independent query interface definition for PHP's MySQL
|
||||
// extension.
|
||||
//
|
||||
|
||||
//
|
||||
// XXX legend:
|
||||
//
|
||||
// XXX ERRORMSG: The error message from the mysql function should
|
||||
// be registered here.
|
||||
//
|
||||
|
||||
require_once "DB/common.php";
|
||||
|
||||
class DB_mysql extends DB_common
|
||||
{
|
||||
// {{{ properties
|
||||
|
||||
var $connection;
|
||||
var $phptype, $dbsyntax;
|
||||
var $prepare_tokens = array();
|
||||
var $prepare_types = array();
|
||||
var $num_rows = array();
|
||||
var $transaction_opcount = 0;
|
||||
var $autocommit = true;
|
||||
var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
|
||||
var $_db = false;
|
||||
|
||||
// }}}
|
||||
// {{{ constructor
|
||||
|
||||
/**
|
||||
* DB_mysql constructor.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
|
||||
function DB_mysql()
|
||||
{
|
||||
$this->DB_common();
|
||||
$this->phptype = 'mysql';
|
||||
$this->dbsyntax = 'mysql';
|
||||
$this->features = array(
|
||||
'prepare' => false,
|
||||
'pconnect' => true,
|
||||
'transactions' => true,
|
||||
'limit' => 'alter'
|
||||
);
|
||||
$this->errorcode_map = array(
|
||||
1004 => DB_ERROR_CANNOT_CREATE,
|
||||
1005 => DB_ERROR_CANNOT_CREATE,
|
||||
1006 => DB_ERROR_CANNOT_CREATE,
|
||||
1007 => DB_ERROR_ALREADY_EXISTS,
|
||||
1008 => DB_ERROR_CANNOT_DROP,
|
||||
1046 => DB_ERROR_NODBSELECTED,
|
||||
1050 => DB_ERROR_ALREADY_EXISTS,
|
||||
1051 => DB_ERROR_NOSUCHTABLE,
|
||||
1054 => DB_ERROR_NOSUCHFIELD,
|
||||
1062 => DB_ERROR_ALREADY_EXISTS,
|
||||
1064 => DB_ERROR_SYNTAX,
|
||||
1100 => DB_ERROR_NOT_LOCKED,
|
||||
1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
|
||||
1146 => DB_ERROR_NOSUCHTABLE,
|
||||
1048 => DB_ERROR_CONSTRAINT,
|
||||
);
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ connect()
|
||||
|
||||
/**
|
||||
* Connect to a database and log in as the specified user.
|
||||
*
|
||||
* @param $dsn the data source name (see DB::parseDSN for syntax)
|
||||
* @param $persistent (optional) whether the connection should
|
||||
* be persistent
|
||||
* @access public
|
||||
* @return int DB_OK on success, a DB error on failure
|
||||
*/
|
||||
|
||||
function connect($dsninfo, $persistent = false)
|
||||
{
|
||||
if (!DB::assertExtension('mysql'))
|
||||
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
|
||||
|
||||
$this->dsn = $dsninfo;
|
||||
if (isset($dsninfo['protocol']) && $dsninfo['protocol'] == 'unix') {
|
||||
$dbhost = ':' . $dsninfo['socket'];
|
||||
} else {
|
||||
$dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
|
||||
if (!empty($dsninfo['port'])) {
|
||||
$dbhost .= ':' . $dsninfo['port'];
|
||||
}
|
||||
}
|
||||
$user = $dsninfo['username'];
|
||||
$pw = $dsninfo['password'];
|
||||
|
||||
$connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
|
||||
|
||||
if ($dbhost && $user && $pw) {
|
||||
$conn = @$connect_function($dbhost, $user, $pw);
|
||||
} elseif ($dbhost && $user) {
|
||||
$conn = @$connect_function($dbhost, $user);
|
||||
} elseif ($dbhost) {
|
||||
$conn = @$connect_function($dbhost);
|
||||
} else {
|
||||
$conn = false;
|
||||
}
|
||||
if (empty($conn)) {
|
||||
if (($err = @mysql_error()) != '') {
|
||||
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
|
||||
null, $err);
|
||||
} elseif (empty($php_errormsg)) {
|
||||
return $this->raiseError(DB_ERROR_CONNECT_FAILED);
|
||||
} else {
|
||||
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
|
||||
null, $php_errormsg);
|
||||
}
|
||||
}
|
||||
|
||||
if ($dsninfo['database']) {
|
||||
if (!@mysql_select_db($dsninfo['database'], $conn)) {
|
||||
switch(mysql_errno($conn)) {
|
||||
case 1049:
|
||||
return $this->raiseError(DB_ERROR_NOSUCHDB, null, null,
|
||||
null, mysql_error($conn));
|
||||
break;
|
||||
case 1044:
|
||||
return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null,
|
||||
null, mysql_error($conn));
|
||||
break;
|
||||
default:
|
||||
return $this->raiseError(DB_ERROR, null, null,
|
||||
null, mysql_error($conn));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
// fix to allow calls to different databases in the same script
|
||||
$this->_db = $dsninfo['database'];
|
||||
}
|
||||
|
||||
$this->connection = $conn;
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ disconnect()
|
||||
|
||||
/**
|
||||
* Log out and disconnect from the database.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool TRUE on success, FALSE if not connected.
|
||||
*/
|
||||
function disconnect()
|
||||
{
|
||||
$ret = mysql_close($this->connection);
|
||||
$this->connection = null;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ simpleQuery()
|
||||
|
||||
/**
|
||||
* Send a query to MySQL and return the results as a MySQL resource
|
||||
* identifier.
|
||||
*
|
||||
* @param the SQL query
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return mixed returns a valid MySQL result for successful SELECT
|
||||
* queries, DB_OK for other successful queries. A DB error is
|
||||
* returned on failure.
|
||||
*/
|
||||
function simpleQuery($query)
|
||||
{
|
||||
$ismanip = DB::isManip($query);
|
||||
$this->last_query = $query;
|
||||
$query = $this->modifyQuery($query);
|
||||
if ($this->_db) {
|
||||
if (!@mysql_select_db($this->_db, $this->connection)) {
|
||||
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
|
||||
}
|
||||
}
|
||||
if (!$this->autocommit && $ismanip) {
|
||||
if ($this->transaction_opcount == 0) {
|
||||
$result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
|
||||
$result = @mysql_query('BEGIN', $this->connection);
|
||||
if (!$result) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
}
|
||||
$this->transaction_opcount++;
|
||||
}
|
||||
$result = @mysql_query($query, $this->connection);
|
||||
if (!$result) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
if (is_resource($result)) {
|
||||
$numrows = $this->numrows($result);
|
||||
if (is_object($numrows)) {
|
||||
return $numrows;
|
||||
}
|
||||
$this->num_rows[$result] = $numrows;
|
||||
return $result;
|
||||
}
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ nextResult()
|
||||
|
||||
/**
|
||||
* Move the internal mysql result pointer to the next available result
|
||||
*
|
||||
* This method has not been implemented yet.
|
||||
*
|
||||
* @param a valid sql result resource
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
function nextResult($result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ fetchInto()
|
||||
|
||||
/**
|
||||
* Fetch a row and insert the data into an existing array.
|
||||
*
|
||||
* @param $result MySQL result identifier
|
||||
* @param $arr (reference) array where data from the row is stored
|
||||
* @param $fetchmode how the array data should be indexed
|
||||
* @param $rownum the row number to fetch
|
||||
* @access public
|
||||
*
|
||||
* @return int DB_OK on success, a DB error on failure
|
||||
*/
|
||||
function fetchInto($result, &$arr, $fetchmode, $rownum=null)
|
||||
{
|
||||
if ($rownum !== null) {
|
||||
if (!@mysql_data_seek($result, $rownum)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ($fetchmode & DB_FETCHMODE_ASSOC) {
|
||||
$arr = @mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
} else {
|
||||
$arr = @mysql_fetch_row($result);
|
||||
}
|
||||
if (!$arr) {
|
||||
// See: http://bugs.php.net/bug.php?id=22328
|
||||
// for why we can't check errors on fetching
|
||||
return null;
|
||||
/*
|
||||
$errno = @mysql_errno($this->connection);
|
||||
if (!$errno) {
|
||||
return NULL;
|
||||
}
|
||||
return $this->mysqlRaiseError($errno);
|
||||
*/
|
||||
}
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ freeResult()
|
||||
|
||||
/**
|
||||
* Free the internal resources associated with $result.
|
||||
*
|
||||
* @param $result MySQL result identifier or DB statement identifier
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool TRUE on success, FALSE if $result is invalid
|
||||
*/
|
||||
function freeResult($result)
|
||||
{
|
||||
if (is_resource($result)) {
|
||||
return mysql_free_result($result);
|
||||
}
|
||||
|
||||
$result = (int)$result; // $result is a prepared query handle
|
||||
if (!isset($this->prepare_tokens[$result])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// I fixed the unset thing.
|
||||
|
||||
$this->prepare_types = array();
|
||||
$this->prepare_tokens = array();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ numCols()
|
||||
|
||||
/**
|
||||
* Get the number of columns in a result set.
|
||||
*
|
||||
* @param $result MySQL result identifier
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return int the number of columns per row in $result
|
||||
*/
|
||||
function numCols($result)
|
||||
{
|
||||
$cols = @mysql_num_fields($result);
|
||||
|
||||
if (!$cols) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
|
||||
return $cols;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ numRows()
|
||||
|
||||
/**
|
||||
* Get the number of rows in a result set.
|
||||
*
|
||||
* @param $result MySQL result identifier
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return int the number of rows in $result
|
||||
*/
|
||||
function numRows($result)
|
||||
{
|
||||
$rows = @mysql_num_rows($result);
|
||||
if ($rows === null) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ autoCommit()
|
||||
|
||||
/**
|
||||
* Enable/disable automatic commits
|
||||
*/
|
||||
function autoCommit($onoff = false)
|
||||
{
|
||||
// XXX if $this->transaction_opcount > 0, we should probably
|
||||
// issue a warning here.
|
||||
$this->autocommit = $onoff ? true : false;
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ commit()
|
||||
|
||||
/**
|
||||
* Commit the current transaction.
|
||||
*/
|
||||
function commit()
|
||||
{
|
||||
if ($this->transaction_opcount > 0) {
|
||||
if ($this->_db) {
|
||||
if (!@mysql_select_db($this->_db, $this->connection)) {
|
||||
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
|
||||
}
|
||||
}
|
||||
$result = @mysql_query('COMMIT', $this->connection);
|
||||
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
|
||||
$this->transaction_opcount = 0;
|
||||
if (!$result) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
}
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ rollback()
|
||||
|
||||
/**
|
||||
* Roll back (undo) the current transaction.
|
||||
*/
|
||||
function rollback()
|
||||
{
|
||||
if ($this->transaction_opcount > 0) {
|
||||
if ($this->_db) {
|
||||
if (!@mysql_select_db($this->_db, $this->connection)) {
|
||||
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
|
||||
}
|
||||
}
|
||||
$result = @mysql_query('ROLLBACK', $this->connection);
|
||||
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
|
||||
$this->transaction_opcount = 0;
|
||||
if (!$result) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
}
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ affectedRows()
|
||||
|
||||
/**
|
||||
* Gets the number of rows affected by the data manipulation
|
||||
* query. For other queries, this function returns 0.
|
||||
*
|
||||
* @return number of rows affected by the last query
|
||||
*/
|
||||
|
||||
function affectedRows()
|
||||
{
|
||||
if (DB::isManip($this->last_query)) {
|
||||
$result = @mysql_affected_rows($this->connection);
|
||||
} else {
|
||||
$result = 0;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ errorNative()
|
||||
|
||||
/**
|
||||
* Get the native error code of the last error (if any) that
|
||||
* occured on the current connection.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return int native MySQL error code
|
||||
*/
|
||||
|
||||
function errorNative()
|
||||
{
|
||||
return mysql_errno($this->connection);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ nextId()
|
||||
|
||||
/**
|
||||
* Get the next value in a sequence. We emulate sequences
|
||||
* for MySQL. Will create the sequence if it does not exist.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param string $seq_name the name of the sequence
|
||||
*
|
||||
* @param bool $ondemand whether to create the sequence table on demand
|
||||
* (default is true)
|
||||
*
|
||||
* @return mixed a sequence integer, or a DB error
|
||||
*/
|
||||
function nextId($seq_name, $ondemand = true)
|
||||
{
|
||||
$seqname = $this->getSequenceName($seq_name);
|
||||
do {
|
||||
$repeat = 0;
|
||||
$this->pushErrorHandling(PEAR_ERROR_RETURN);
|
||||
$result = $this->query("UPDATE ${seqname} ".
|
||||
'SET id=LAST_INSERT_ID(id+1)');
|
||||
$this->popErrorHandling();
|
||||
if ($result == DB_OK) {
|
||||
/** COMMON CASE **/
|
||||
$id = mysql_insert_id($this->connection);
|
||||
if ($id != 0) {
|
||||
return $id;
|
||||
}
|
||||
/** EMPTY SEQ TABLE **/
|
||||
// Sequence table must be empty for some reason, so fill it and return 1
|
||||
// Obtain a user-level lock
|
||||
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
|
||||
if (DB::isError($result)) {
|
||||
return $this->raiseError($result);
|
||||
}
|
||||
if ($result == 0) {
|
||||
// Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error
|
||||
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
|
||||
}
|
||||
|
||||
// add the default value
|
||||
$result = $this->query("REPLACE INTO ${seqname} VALUES (0)");
|
||||
if (DB::isError($result)) {
|
||||
return $this->raiseError($result);
|
||||
}
|
||||
|
||||
// Release the lock
|
||||
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
|
||||
if (DB::isError($result)) {
|
||||
return $this->raiseError($result);
|
||||
}
|
||||
// We know what the result will be, so no need to try again
|
||||
return 1;
|
||||
|
||||
/** ONDEMAND TABLE CREATION **/
|
||||
} 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;
|
||||
}
|
||||
|
||||
/** BACKWARDS COMPAT **/
|
||||
} elseif (DB::isError($result) &&
|
||||
$result->getCode() == DB_ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
// see _BCsequence() comment
|
||||
$result = $this->_BCsequence($seqname);
|
||||
if (DB::isError($result)) {
|
||||
return $this->raiseError($result);
|
||||
}
|
||||
$repeat = 1;
|
||||
}
|
||||
} while ($repeat);
|
||||
|
||||
return $this->raiseError($result);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ createSequence()
|
||||
|
||||
function createSequence($seq_name)
|
||||
{
|
||||
$seqname = $this->getSequenceName($seq_name);
|
||||
$res = $this->query("CREATE TABLE ${seqname} ".
|
||||
'(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
|
||||
' PRIMARY KEY(id))');
|
||||
if (DB::isError($res)) {
|
||||
return $res;
|
||||
}
|
||||
// insert yields value 1, nextId call will generate ID 2
|
||||
$res = $this->query("INSERT INTO ${seqname} VALUES(0)");
|
||||
if (DB::isError($res)) {
|
||||
return $res;
|
||||
}
|
||||
// so reset to zero
|
||||
return $this->query("UPDATE ${seqname} SET id = 0;");
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ dropSequence()
|
||||
|
||||
function dropSequence($seq_name)
|
||||
{
|
||||
$seqname = $this->getSequenceName($seq_name);
|
||||
return $this->query("DROP TABLE ${seqname}");
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _BCsequence()
|
||||
|
||||
/**
|
||||
* Backwards compatibility with old sequence emulation implementation
|
||||
* (clean up the dupes)
|
||||
*
|
||||
* @param string $seqname The sequence name to clean up
|
||||
* @return mixed DB_Error or true
|
||||
*/
|
||||
function _BCsequence($seqname)
|
||||
{
|
||||
// Obtain a user-level lock... this will release any previous
|
||||
// application locks, but unlike LOCK TABLES, it does not abort
|
||||
// the current transaction and is much less frequently used.
|
||||
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
|
||||
if (DB::isError($result)) {
|
||||
return $result;
|
||||
}
|
||||
if ($result == 0) {
|
||||
// Failed to get the lock, can't do the conversion, bail
|
||||
// with a DB_ERROR_NOT_LOCKED error
|
||||
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
|
||||
}
|
||||
|
||||
$highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
|
||||
if (DB::isError($highest_id)) {
|
||||
return $highest_id;
|
||||
}
|
||||
// This should kill all rows except the highest
|
||||
// We should probably do something if $highest_id isn't
|
||||
// numeric, but I'm at a loss as how to handle that...
|
||||
$result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id");
|
||||
if (DB::isError($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// If another thread has been waiting for this lock,
|
||||
// it will go thru the above procedure, but will have no
|
||||
// real effect
|
||||
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
|
||||
if (DB::isError($result)) {
|
||||
return $result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ quote()
|
||||
|
||||
/**
|
||||
* Quote the given string so it can be safely used within string delimiters
|
||||
* in a query.
|
||||
* @param $string mixed Data to be quoted
|
||||
* @return mixed "NULL" string, quoted string or original data
|
||||
*/
|
||||
function quote($str = null)
|
||||
{
|
||||
switch (strtolower(gettype($str))) {
|
||||
case 'null':
|
||||
return 'NULL';
|
||||
case 'integer':
|
||||
case 'double':
|
||||
return $str;
|
||||
case 'string':
|
||||
default:
|
||||
if(function_exists('mysql_real_escape_string')) {
|
||||
return "'".mysql_real_escape_string($str, $this->connection)."'";
|
||||
} else {
|
||||
return "'".mysql_escape_string($str)."'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ modifyQuery()
|
||||
|
||||
function modifyQuery($query, $subject = null)
|
||||
{
|
||||
if ($this->options['optimize'] == 'portability') {
|
||||
// "DELETE FROM table" gives 0 affected rows in MySQL.
|
||||
// This little hack lets you know how many rows were deleted.
|
||||
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;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ modifyLimitQuery()
|
||||
|
||||
function modifyLimitQuery($query, $from, $count)
|
||||
{
|
||||
if (DB::isManip($query)) {
|
||||
return $query . " LIMIT $count";
|
||||
} else {
|
||||
return $query . " LIMIT $from, $count";
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ mysqlRaiseError()
|
||||
|
||||
function mysqlRaiseError($errno = null)
|
||||
{
|
||||
if ($errno === null) {
|
||||
$errno = $this->errorCode(mysql_errno($this->connection));
|
||||
}
|
||||
return $this->raiseError($errno, null, null, null,
|
||||
@mysql_errno($this->connection) . " ** " .
|
||||
@mysql_error($this->connection));
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ tableInfo()
|
||||
|
||||
function tableInfo($result, $mode = null) {
|
||||
$count = 0;
|
||||
$id = 0;
|
||||
$res = array();
|
||||
|
||||
/*
|
||||
* depending on $mode, metadata returns the following values:
|
||||
*
|
||||
* - mode is null (default):
|
||||
* $result[]:
|
||||
* [0]["table"] table name
|
||||
* [0]["name"] field name
|
||||
* [0]["type"] field type
|
||||
* [0]["len"] field length
|
||||
* [0]["flags"] field flags
|
||||
*
|
||||
* - mode is DB_TABLEINFO_ORDER
|
||||
* $result[]:
|
||||
* ["num_fields"] number of metadata records
|
||||
* [0]["table"] table name
|
||||
* [0]["name"] field name
|
||||
* [0]["type"] field type
|
||||
* [0]["len"] field length
|
||||
* [0]["flags"] field flags
|
||||
* ["order"][field name] index of field named "field name"
|
||||
* The last one is used, if you have a field name, but no index.
|
||||
* Test: if (isset($result['meta']['myfield'])) { ...
|
||||
*
|
||||
* - mode is DB_TABLEINFO_ORDERTABLE
|
||||
* the same as above. but additionally
|
||||
* ["ordertable"][table name][field name] index of field
|
||||
* named "field name"
|
||||
*
|
||||
* this is, because if you have fields from different
|
||||
* tables with the same field name * they override each
|
||||
* other with DB_TABLEINFO_ORDER
|
||||
*
|
||||
* you can combine DB_TABLEINFO_ORDER and
|
||||
* DB_TABLEINFO_ORDERTABLE with DB_TABLEINFO_ORDER |
|
||||
* DB_TABLEINFO_ORDERTABLE * or with DB_TABLEINFO_FULL
|
||||
*/
|
||||
|
||||
// if $result is a string, then we want information about a
|
||||
// table without a resultset
|
||||
if (is_string($result)) {
|
||||
$id = @mysql_list_fields($this->dsn['database'],
|
||||
$result, $this->connection);
|
||||
if (empty($id)) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
} else { // else we want information about a resultset
|
||||
$id = $result;
|
||||
if (empty($id)) {
|
||||
return $this->mysqlRaiseError();
|
||||
}
|
||||
}
|
||||
|
||||
$count = @mysql_num_fields($id);
|
||||
|
||||
// made this IF due to performance (one if is faster than $count if's)
|
||||
if (empty($mode)) {
|
||||
for ($i=0; $i<$count; $i++) {
|
||||
$res[$i]['table'] = @mysql_field_table ($id, $i);
|
||||
$res[$i]['name'] = @mysql_field_name ($id, $i);
|
||||
$res[$i]['type'] = @mysql_field_type ($id, $i);
|
||||
$res[$i]['len'] = @mysql_field_len ($id, $i);
|
||||
$res[$i]['flags'] = @mysql_field_flags ($id, $i);
|
||||
}
|
||||
} else { // full
|
||||
$res['num_fields']= $count;
|
||||
|
||||
for ($i=0; $i<$count; $i++) {
|
||||
$res[$i]['table'] = @mysql_field_table ($id, $i);
|
||||
$res[$i]['name'] = @mysql_field_name ($id, $i);
|
||||
$res[$i]['type'] = @mysql_field_type ($id, $i);
|
||||
$res[$i]['len'] = @mysql_field_len ($id, $i);
|
||||
$res[$i]['flags'] = @mysql_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 (is_string($result)) {
|
||||
@mysql_free_result($id);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getSpecialQuery()
|
||||
|
||||
/**
|
||||
* Returns the query needed to get some backend info
|
||||
* @param string $type What kind of info you want to retrieve
|
||||
* @return string The SQL query string
|
||||
*/
|
||||
function getSpecialQuery($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'tables':
|
||||
$sql = "SHOW TABLES";
|
||||
break;
|
||||
case 'views':
|
||||
return DB_ERROR_NOT_CAPABLE;
|
||||
case 'users':
|
||||
$sql = "select distinct User from user";
|
||||
if($this->dsn['database'] != 'mysql') {
|
||||
$dsn = $this->dsn;
|
||||
$dsn['database'] = 'mysql';
|
||||
if (DB::isError($db = DB::connect($dsn))) {
|
||||
return $db;
|
||||
}
|
||||
$sql = $db->getCol($sql);
|
||||
$db->disconnect();
|
||||
// XXX Fixme the mysql driver should take care of this
|
||||
if (!@mysql_select_db($this->dsn['database'], $this->connection)) {
|
||||
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
|
||||
}
|
||||
}
|
||||
return $sql;
|
||||
break;
|
||||
case 'databases':
|
||||
$sql = "SHOW DATABASES";
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// TODO/wishlist:
|
||||
// longReadlen
|
||||
// binmode
|
||||
}
|
||||
|
||||
?>
|
||||
786
htdocs/includes/pear/DB/pgsql.php
Normal file
786
htdocs/includes/pear/DB/pgsql.php
Normal file
@ -0,0 +1,786 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
|
||||
// +----------------------------------------------------------------------+
|
||||
// | PHP Version 4 |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Copyright (c) 1997-2003 The PHP Group |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | This source file is subject to version 2.02 of the PHP license, |
|
||||
// | that is bundled with this package in the file LICENSE, and is |
|
||||
// | available at through the world-wide-web at |
|
||||
// | http://www.php.net/license/2_02.txt. |
|
||||
// | If you did not receive a copy of the PHP license and are unable to |
|
||||
// | obtain it through the world-wide-web, please send a note to |
|
||||
// | license@php.net so we can mail you a copy immediately. |
|
||||
// +----------------------------------------------------------------------+
|
||||
// | Authors: Rui Hirokawa <rui_hirokawa@ybb.ne.jp> |
|
||||
// | Stig Bakken <ssb@php.net> |
|
||||
// +----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Database independent query interface definition for PHP's PostgreSQL
|
||||
// extension.
|
||||
//
|
||||
|
||||
require_once 'DB/common.php';
|
||||
|
||||
class DB_pgsql extends DB_common
|
||||
{
|
||||
// {{{ properties
|
||||
|
||||
var $connection;
|
||||
var $phptype, $dbsyntax;
|
||||
var $prepare_tokens = array();
|
||||
var $prepare_types = array();
|
||||
var $transaction_opcount = 0;
|
||||
var $dsn = array();
|
||||
var $row = array();
|
||||
var $num_rows = array();
|
||||
var $affected = 0;
|
||||
var $autocommit = true;
|
||||
var $fetchmode = DB_FETCHMODE_ORDERED;
|
||||
|
||||
// }}}
|
||||
// {{{ constructor
|
||||
|
||||
function DB_pgsql()
|
||||
{
|
||||
$this->DB_common();
|
||||
$this->phptype = 'pgsql';
|
||||
$this->dbsyntax = 'pgsql';
|
||||
$this->features = array(
|
||||
'prepare' => false,
|
||||
'pconnect' => true,
|
||||
'transactions' => true,
|
||||
'limit' => 'alter'
|
||||
);
|
||||
$this->errorcode_map = array(
|
||||
);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ connect()
|
||||
|
||||
/**
|
||||
* Connect to a database and log in as the specified user.
|
||||
*
|
||||
* @param $dsn the data source name (see DB::parseDSN for syntax)
|
||||
* @param $persistent (optional) whether the connection should
|
||||
* be persistent
|
||||
*
|
||||
* @return int DB_OK on success, a DB error code on failure
|
||||
*/
|
||||
function connect($dsninfo, $persistent = false)
|
||||
{
|
||||
if (!DB::assertExtension('pgsql'))
|
||||
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
|
||||
|
||||
$this->dsn = $dsninfo;
|
||||
$protocol = (isset($dsninfo['protocol'])) ? $dsninfo['protocol'] : 'tcp';
|
||||
$connstr = '';
|
||||
|
||||
if ($protocol == 'tcp') {
|
||||
if (!empty($dsninfo['hostspec'])) {
|
||||
$connstr = 'host=' . $dsninfo['hostspec'];
|
||||
}
|
||||
if (!empty($dsninfo['port'])) {
|
||||
$connstr .= ' port=' . $dsninfo['port'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($dsninfo['database'])) {
|
||||
$connstr .= ' dbname=\'' . addslashes($dsninfo['database']) . '\'';
|
||||
}
|
||||
if (!empty($dsninfo['username'])) {
|
||||
$connstr .= ' user=\'' . addslashes($dsninfo['username']) . '\'';
|
||||
}
|
||||
if (!empty($dsninfo['password'])) {
|
||||
$connstr .= ' password=\'' . addslashes($dsninfo['password']) . '\'';
|
||||
}
|
||||
if (!empty($dsninfo['options'])) {
|
||||
$connstr .= ' options=' . $dsninfo['options'];
|
||||
}
|
||||
if (!empty($dsninfo['tty'])) {
|
||||
$connstr .= ' tty=' . $dsninfo['tty'];
|
||||
}
|
||||
|
||||
$connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
|
||||
// catch error
|
||||
ob_start();
|
||||
$conn = $connect_function($connstr);
|
||||
$error = ob_get_contents();
|
||||
ob_end_clean();
|
||||
if ($conn == false) {
|
||||
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null,
|
||||
null, null, strip_tags($error));
|
||||
}
|
||||
$this->connection = $conn;
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ disconnect()
|
||||
|
||||
/**
|
||||
* Log out and disconnect from the database.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE if not connected.
|
||||
*/
|
||||
function disconnect()
|
||||
{
|
||||
$ret = @pg_close($this->connection);
|
||||
$this->connection = null;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ simpleQuery()
|
||||
|
||||
/**
|
||||
* Send a query to PostgreSQL and return the results as a
|
||||
* PostgreSQL resource identifier.
|
||||
*
|
||||
* @param $query the SQL query
|
||||
*
|
||||
* @return int returns a valid PostgreSQL result for successful SELECT
|
||||
* queries, DB_OK for other successful queries. A DB error code
|
||||
* is returned on failure.
|
||||
*/
|
||||
function simpleQuery($query)
|
||||
{
|
||||
$ismanip = DB::isManip($query);
|
||||
$this->last_query = $query;
|
||||
$query = $this->modifyQuery($query);
|
||||
if (!$this->autocommit && $ismanip) {
|
||||
if ($this->transaction_opcount == 0) {
|
||||
$result = @pg_exec($this->connection, "begin;");
|
||||
if (!$result) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
}
|
||||
$this->transaction_opcount++;
|
||||
}
|
||||
$result = @pg_exec($this->connection, $query);
|
||||
if (!$result) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
// Determine which queries that should return data, and which
|
||||
// should return an error code only.
|
||||
if ($ismanip) {
|
||||
$this->affected = @pg_cmdtuples($result);
|
||||
return DB_OK;
|
||||
} elseif (preg_match('/^\s*\(?\s*SELECT\s+/si', $query) &&
|
||||
!preg_match('/^\s*\(?\s*SELECT\s+INTO\s/si', $query)) {
|
||||
/* PostgreSQL commands:
|
||||
ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
|
||||
CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
|
||||
GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
|
||||
REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
|
||||
UNLISTEN, UPDATE, VACUUM
|
||||
*/
|
||||
$this->row[$result] = 0; // reset the row counter.
|
||||
$numrows = $this->numrows($result);
|
||||
if (is_object($numrows)) {
|
||||
return $numrows;
|
||||
}
|
||||
$this->num_rows[$result] = $numrows;
|
||||
$this->affected = 0;
|
||||
return $result;
|
||||
} else {
|
||||
$this->affected = 0;
|
||||
return DB_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ nextResult()
|
||||
|
||||
/**
|
||||
* Move the internal pgsql result pointer to the next available result
|
||||
*
|
||||
* @param a valid fbsql result resource
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return true if a result is available otherwise return false
|
||||
*/
|
||||
function nextResult($result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ errorCode()
|
||||
|
||||
/**
|
||||
* Map native error codes to DB's portable ones. Requires that
|
||||
* the DB implementation's constructor fills in the $errorcode_map
|
||||
* property.
|
||||
*
|
||||
* @param $nativecode the native error code, as returned by the backend
|
||||
* database extension (string or integer)
|
||||
*
|
||||
* @return int a portable DB error code, or FALSE if this DB
|
||||
* implementation has no mapping for the given error code.
|
||||
*/
|
||||
|
||||
function errorCode($errormsg)
|
||||
{
|
||||
static $error_regexps;
|
||||
if (empty($error_regexps)) {
|
||||
$error_regexps = array(
|
||||
'/(Table does not exist\.|Relation [\"\'].*[\"\'] does not exist|sequence does not exist|class ".+" not found)$/' => DB_ERROR_NOSUCHTABLE,
|
||||
'/table [\"\'].*[\"\'] does not exist/' => DB_ERROR_NOSUCHTABLE,
|
||||
'/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/' => DB_ERROR_ALREADY_EXISTS,
|
||||
'/divide by zero$/' => DB_ERROR_DIVZERO,
|
||||
'/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER,
|
||||
'/ttribute [\"\'].*[\"\'] not found$|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => DB_ERROR_NOSUCHFIELD,
|
||||
'/parser: parse error at or near \"/' => DB_ERROR_SYNTAX,
|
||||
'/referential integrity violation/' => DB_ERROR_CONSTRAINT
|
||||
);
|
||||
}
|
||||
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()
|
||||
|
||||
/**
|
||||
* Fetch a row and insert the data into an existing array.
|
||||
*
|
||||
* @param $result PostgreSQL result identifier
|
||||
* @param $row (reference) array where data from the row is stored
|
||||
* @param $fetchmode how the array data should be indexed
|
||||
* @param $rownum the row number to fetch
|
||||
*
|
||||
* @return int DB_OK on success, a DB error code on failure
|
||||
*/
|
||||
function fetchInto($result, &$row, $fetchmode, $rownum=null)
|
||||
{
|
||||
$rownum = ($rownum !== null) ? $rownum : $this->row[$result];
|
||||
if ($rownum >= $this->num_rows[$result]) {
|
||||
return null;
|
||||
}
|
||||
if ($fetchmode & DB_FETCHMODE_ASSOC) {
|
||||
$row = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
|
||||
} else {
|
||||
$row = @pg_fetch_row($result, $rownum);
|
||||
}
|
||||
if (!$row) {
|
||||
$err = pg_errormessage($this->connection);
|
||||
if (!$err) {
|
||||
return null;
|
||||
}
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
$this->row[$result] = ++$rownum;
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ freeResult()
|
||||
|
||||
/**
|
||||
* Free the internal resources associated with $result.
|
||||
*
|
||||
* @param $result int PostgreSQL result identifier or DB statement identifier
|
||||
*
|
||||
* @return bool TRUE on success, FALSE if $result is invalid
|
||||
*/
|
||||
function freeResult($result)
|
||||
{
|
||||
if (is_resource($result)) {
|
||||
return @pg_freeresult($result);
|
||||
}
|
||||
if (!isset($this->prepare_tokens[(int)$result])) {
|
||||
return false;
|
||||
}
|
||||
unset($this->prepare_tokens[(int)$result]);
|
||||
unset($this->prepare_types[(int)$result]);
|
||||
unset($this->row[(int)$result]);
|
||||
unset($this->num_rows[(int)$result]);
|
||||
$this->affected = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ quote()
|
||||
/**
|
||||
* Quote the given string so it can be safely used within string delimiters
|
||||
* in a query.
|
||||
* @param $string mixed Data to be quoted
|
||||
* @return mixed "NULL" string, quoted string or original data
|
||||
*/
|
||||
function quote($str = null)
|
||||
{
|
||||
switch (strtolower(gettype($str))) {
|
||||
case 'null':
|
||||
return 'NULL';
|
||||
case 'integer':
|
||||
case 'double' :
|
||||
return $str;
|
||||
case 'boolean':
|
||||
return $str ? 'TRUE' : 'FALSE';
|
||||
case 'string':
|
||||
default:
|
||||
$str = str_replace("'", "''", $str);
|
||||
//PostgreSQL treats a backslash as an escape character.
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
return "'$str'";
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// {{{ numCols()
|
||||
|
||||
/**
|
||||
* Get the number of columns in a result set.
|
||||
*
|
||||
* @param $result resource PostgreSQL result identifier
|
||||
*
|
||||
* @return int the number of columns per row in $result
|
||||
*/
|
||||
function numCols($result)
|
||||
{
|
||||
$cols = @pg_numfields($result);
|
||||
if (!$cols) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
return $cols;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ numRows()
|
||||
|
||||
/**
|
||||
* Get the number of rows in a result set.
|
||||
*
|
||||
* @param $result resource PostgreSQL result identifier
|
||||
*
|
||||
* @return int the number of rows in $result
|
||||
*/
|
||||
function numRows($result)
|
||||
{
|
||||
$rows = @pg_numrows($result);
|
||||
if ($rows === null) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ errorNative()
|
||||
|
||||
/**
|
||||
* Get the native error code of the last error (if any) that
|
||||
* occured on the current connection.
|
||||
*
|
||||
* @return int native PostgreSQL error code
|
||||
*/
|
||||
function errorNative()
|
||||
{
|
||||
return pg_errormessage($this->connection);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ autoCommit()
|
||||
|
||||
/**
|
||||
* Enable/disable automatic commits
|
||||
*/
|
||||
function autoCommit($onoff = false)
|
||||
{
|
||||
// XXX if $this->transaction_opcount > 0, we should probably
|
||||
// issue a warning here.
|
||||
$this->autocommit = $onoff ? true : false;
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ commit()
|
||||
|
||||
/**
|
||||
* Commit the current transaction.
|
||||
*/
|
||||
function commit()
|
||||
{
|
||||
if ($this->transaction_opcount > 0) {
|
||||
// (disabled) hack to shut up error messages from libpq.a
|
||||
//@fclose(@fopen("php://stderr", "w"));
|
||||
$result = @pg_exec($this->connection, "end;");
|
||||
$this->transaction_opcount = 0;
|
||||
if (!$result) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
}
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ rollback()
|
||||
|
||||
/**
|
||||
* Roll back (undo) the current transaction.
|
||||
*/
|
||||
function rollback()
|
||||
{
|
||||
if ($this->transaction_opcount > 0) {
|
||||
$result = @pg_exec($this->connection, "abort;");
|
||||
$this->transaction_opcount = 0;
|
||||
if (!$result) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
}
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ affectedRows()
|
||||
|
||||
/**
|
||||
* Gets the number of rows affected by the last query.
|
||||
* if the last query was a select, returns 0.
|
||||
*
|
||||
* @return int number of rows affected by the last query or DB_ERROR
|
||||
*/
|
||||
function affectedRows()
|
||||
{
|
||||
return $this->affected;
|
||||
}
|
||||
// }}}
|
||||
// {{{ nextId()
|
||||
|
||||
/**
|
||||
* Get the next value in a sequence.
|
||||
*
|
||||
* We are using native PostgreSQL sequences. If a sequence does
|
||||
* not exist, it will be created, unless $ondemand is false.
|
||||
*
|
||||
* @access public
|
||||
* @param string $seq_name the name of the sequence
|
||||
* @param bool $ondemand whether to create the sequence on demand
|
||||
* @return a sequence integer, or a DB error
|
||||
*/
|
||||
function nextId($seq_name, $ondemand = true)
|
||||
{
|
||||
$seqname = $this->getSequenceName($seq_name);
|
||||
$repeat = false;
|
||||
do {
|
||||
$this->pushErrorHandling(PEAR_ERROR_RETURN);
|
||||
$result = $this->query("SELECT NEXTVAL('${seqname}')");
|
||||
$this->popErrorHandling();
|
||||
if ($ondemand && DB::isError($result) &&
|
||||
$result->getCode() == DB_ERROR_NOSUCHTABLE) {
|
||||
$repeat = true;
|
||||
$this->pushErrorHandling(PEAR_ERROR_RETURN);
|
||||
$result = $this->createSequence($seq_name);
|
||||
$this->popErrorHandling();
|
||||
if (DB::isError($result)) {
|
||||
return $this->raiseError($result);
|
||||
}
|
||||
} else {
|
||||
$repeat = false;
|
||||
}
|
||||
} while ($repeat);
|
||||
if (DB::isError($result)) {
|
||||
return $this->raiseError($result);
|
||||
}
|
||||
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
|
||||
$result->free();
|
||||
return $arr[0];
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ createSequence()
|
||||
|
||||
/**
|
||||
* Create the sequence
|
||||
*
|
||||
* @param string $seq_name the name of the sequence
|
||||
* @return mixed DB_OK on success or DB error on error
|
||||
* @access public
|
||||
*/
|
||||
function createSequence($seq_name)
|
||||
{
|
||||
$seqname = $this->getSequenceName($seq_name);
|
||||
$result = $this->query("CREATE SEQUENCE ${seqname}");
|
||||
return $result;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ dropSequence()
|
||||
|
||||
/**
|
||||
* Drop a sequence
|
||||
*
|
||||
* @param string $seq_name the name of the sequence
|
||||
* @return mixed DB_OK on success or DB error on error
|
||||
* @access public
|
||||
*/
|
||||
function dropSequence($seq_name)
|
||||
{
|
||||
$seqname = $this->getSequenceName($seq_name);
|
||||
return $this->query("DROP SEQUENCE ${seqname}");
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ modifyLimitQuery()
|
||||
|
||||
function modifyLimitQuery($query, $from, $count)
|
||||
{
|
||||
$query = $query . " LIMIT $count OFFSET $from";
|
||||
return $query;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ pgsqlRaiseError()
|
||||
|
||||
function pgsqlRaiseError($errno = null)
|
||||
{
|
||||
$native = $this->errorNative();
|
||||
if ($errno === null) {
|
||||
$err = $this->errorCode($native);
|
||||
} else {
|
||||
$err = $errno;
|
||||
}
|
||||
return $this->raiseError($err, null, null, null, $native);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _pgFieldFlags()
|
||||
|
||||
/**
|
||||
* Flags of a Field
|
||||
*
|
||||
* @param int $resource PostgreSQL result identifier
|
||||
* @param int $num_field the field number
|
||||
*
|
||||
* @return string The flags of the field ("not_null", "default_xx", "primary_key",
|
||||
* "unique" and "multiple_key" are supported)
|
||||
* @access private
|
||||
*/
|
||||
function _pgFieldFlags($resource, $num_field, $table_name)
|
||||
{
|
||||
$field_name = @pg_fieldname($resource, $num_field);
|
||||
|
||||
$result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
|
||||
FROM pg_attribute f, pg_class tab, pg_type typ
|
||||
WHERE tab.relname = typ.typname
|
||||
AND typ.typrelid = f.attrelid
|
||||
AND f.attname = '$field_name'
|
||||
AND tab.relname = '$table_name'");
|
||||
if (@pg_numrows($result) > 0) {
|
||||
$row = @pg_fetch_row($result, 0);
|
||||
$flags = ($row[0] == 't') ? 'not_null ' : '';
|
||||
|
||||
if ($row[1] == 't') {
|
||||
$result = @pg_exec($this->connection, "SELECT a.adsrc
|
||||
FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a
|
||||
WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
|
||||
AND f.attrelid = a.adrelid AND f.attname = '$field_name'
|
||||
AND tab.relname = '$table_name' AND f.attnum = a.adnum");
|
||||
$row = @pg_fetch_row($result, 0);
|
||||
$num = str_replace('\'', '', $row[0]);
|
||||
|
||||
$flags .= "default_$num ";
|
||||
}
|
||||
}
|
||||
$result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
|
||||
FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i
|
||||
WHERE tab.relname = typ.typname
|
||||
AND typ.typrelid = f.attrelid
|
||||
AND f.attrelid = i.indrelid
|
||||
AND f.attname = '$field_name'
|
||||
AND tab.relname = '$table_name'");
|
||||
$count = @pg_numrows($result);
|
||||
|
||||
for ($i = 0; $i < $count ; $i++) {
|
||||
$row = @pg_fetch_row($result, $i);
|
||||
$keys = explode(" ", $row[2]);
|
||||
|
||||
if (in_array($num_field + 1, $keys)) {
|
||||
$flags .= ($row[0] == 't') ? 'unique ' : '';
|
||||
$flags .= ($row[1] == 't') ? 'primary ' : '';
|
||||
if (count($keys) > 1)
|
||||
$flags .= 'multiple_key ';
|
||||
}
|
||||
}
|
||||
|
||||
return trim($flags);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ tableInfo()
|
||||
|
||||
/**
|
||||
* Returns information about a table or a result set
|
||||
*
|
||||
* NOTE: doesn't support table name and flags if called from a db_result
|
||||
*
|
||||
* @param mixed $resource PostgreSQL result identifier or table name
|
||||
* @param int $mode A valid tableInfo mode (DB_TABLEINFO_ORDERTABLE or
|
||||
* DB_TABLEINFO_ORDER)
|
||||
*
|
||||
* @return array An array with all the information
|
||||
*/
|
||||
function tableInfo($result, $mode = null)
|
||||
{
|
||||
$count = 0;
|
||||
$id = 0;
|
||||
$res = array();
|
||||
|
||||
/*
|
||||
* depending on $mode, metadata returns the following values:
|
||||
*
|
||||
* - mode is false (default):
|
||||
* $result[]:
|
||||
* [0]["table"] table name
|
||||
* [0]["name"] field name
|
||||
* [0]["type"] field type
|
||||
* [0]["len"] field length
|
||||
* [0]["flags"] field flags
|
||||
*
|
||||
* - mode is DB_TABLEINFO_ORDER
|
||||
* $result[]:
|
||||
* ["num_fields"] number of metadata records
|
||||
* [0]["table"] table name
|
||||
* [0]["name"] field name
|
||||
* [0]["type"] field type
|
||||
* [0]["len"] field length
|
||||
* [0]["flags"] field flags
|
||||
* ["order"][field name] index of field named "field name"
|
||||
* The last one is used, if you have a field name, but no index.
|
||||
* Test: if (isset($result['meta']['myfield'])) { ...
|
||||
*
|
||||
* - mode is DB_TABLEINFO_ORDERTABLE
|
||||
* the same as above. but additionally
|
||||
* ["ordertable"][table name][field name] index of field
|
||||
* named "field name"
|
||||
*
|
||||
* this is, because if you have fields from different
|
||||
* tables with the same field name * they override each
|
||||
* other with DB_TABLEINFO_ORDER
|
||||
*
|
||||
* you can combine DB_TABLEINFO_ORDER and
|
||||
* DB_TABLEINFO_ORDERTABLE with DB_TABLEINFO_ORDER |
|
||||
* DB_TABLEINFO_ORDERTABLE * or with DB_TABLEINFO_FULL
|
||||
*/
|
||||
|
||||
// if $result is a string, then we want information about a
|
||||
// table without a resultset
|
||||
|
||||
if (is_string($result)) {
|
||||
$id = @pg_exec($this->connection,"SELECT * FROM $result LIMIT 0");
|
||||
if (empty($id)) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
} else { // else we want information about a resultset
|
||||
$id = $result;
|
||||
if (empty($id)) {
|
||||
return $this->pgsqlRaiseError();
|
||||
}
|
||||
}
|
||||
|
||||
$count = @pg_numfields($id);
|
||||
|
||||
// made this IF due to performance (one if is faster than $count if's)
|
||||
if (empty($mode)) {
|
||||
|
||||
for ($i=0; $i<$count; $i++) {
|
||||
$res[$i]['table'] = (is_string($result)) ? $result : '';
|
||||
$res[$i]['name'] = @pg_fieldname ($id, $i);
|
||||
$res[$i]['type'] = @pg_fieldtype ($id, $i);
|
||||
$res[$i]['len'] = @pg_fieldsize ($id, $i);
|
||||
$res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldflags($id, $i, $result) : '';
|
||||
}
|
||||
|
||||
} else { // full
|
||||
$res["num_fields"]= $count;
|
||||
|
||||
for ($i=0; $i<$count; $i++) {
|
||||
$res[$i]['table'] = (is_string($result)) ? $result : '';
|
||||
$res[$i]['name'] = @pg_fieldname ($id, $i);
|
||||
$res[$i]['type'] = @pg_fieldtype ($id, $i);
|
||||
$res[$i]['len'] = @pg_fieldsize ($id, $i);
|
||||
$res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldFlags($id, $i, $result) : '';
|
||||
if ($mode & DB_TABLEINFO_ORDER) {
|
||||
$res['order'][$res[$i]['name']] = $i;
|
||||
}
|
||||
if ($mode & DB_TABLEINFO_ORDERTABLE) {
|
||||
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free the result only if we were called on a table
|
||||
if (is_string($result) && is_resource($id)) {
|
||||
@pg_freeresult($id);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getTablesQuery()
|
||||
|
||||
/**
|
||||
* Returns the query needed to get some backend info
|
||||
* @param string $type What kind of info you want to retrieve
|
||||
* @return string The SQL query string
|
||||
*/
|
||||
function getSpecialQuery($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'tables': {
|
||||
$sql = "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_'
|
||||
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_'";
|
||||
break;
|
||||
}
|
||||
case 'views': {
|
||||
// Table cols: viewname | viewowner | definition
|
||||
$sql = "SELECT viewname FROM pg_views";
|
||||
break;
|
||||
}
|
||||
case 'users': {
|
||||
// cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil
|
||||
$sql = 'SELECT usename FROM pg_user';
|
||||
break;
|
||||
}
|
||||
case 'databases': {
|
||||
$sql = 'SELECT datname FROM pg_database';
|
||||
break;
|
||||
}
|
||||
case 'functions': {
|
||||
$sql = 'SELECT proname FROM pg_proc';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
}
|
||||
|
||||
// Local variables:
|
||||
// tab-width: 4
|
||||
// c-basic-offset: 4
|
||||
// End:
|
||||
?>
|
||||
Loading…
Reference in New Issue
Block a user