Nouveaux fichiers
This commit is contained in:
parent
177deca5de
commit
edf52c5e1a
@ -186,7 +186,7 @@ class DOLIAuth {
|
|||||||
$storage_path = "Auth/Container/" . $driver . ".php";
|
$storage_path = "Auth/Container/" . $driver . ".php";
|
||||||
$storage_class = "Auth_Container_" . $driver;
|
$storage_class = "Auth_Container_" . $driver;
|
||||||
|
|
||||||
require_once $storage_path;
|
require_once DOL_DOCUMENT_ROOT."/includes/pear/".$storage_path;
|
||||||
|
|
||||||
return new $storage_class($options);
|
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