Skip to content

Instantly share code, notes, and snippets.

@Teino1978-Corp
Forked from kevin4044/owncloud_.htaccess
Created November 4, 2015 07:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Teino1978-Corp/92a16441bcc4b990fa14 to your computer and use it in GitHub Desktop.
Save Teino1978-Corp/92a16441bcc4b990fa14 to your computer and use it in GitHub Desktop.
owncloud extended version, developing
This gist exceeds the recommended number of files (~10). To access all files, please clone this gist.
ErrorDocument 404 /owncloud/core/templates/404.php
<IfModule mod_php5.c>
php_value upload_max_filesize 512M
php_value post_max_size 512M
php_value memory_limit 128M
SetEnv htaccessWorking true
</IfModule>
Options -Indexes
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" />
<component name="SvnConfiguration" maxAnnotateRevisions="500" myUseAcceleration="nothing" myAutoUpdateAfterCommit="false" cleanupOnStartRun="false" SSL_PROTOCOLS="sslv3">
<option name="USER" value="" />
<option name="PASSWORD" value="" />
<option name="mySSHConnectionTimeout" value="30000" />
<option name="mySSHReadTimeout" value="30000" />
<option name="LAST_MERGED_REVISION" />
<option name="MERGE_DRY_RUN" value="false" />
<option name="MERGE_DIFF_USE_ANCESTRY" value="true" />
<option name="UPDATE_LOCK_ON_DEMAND" value="false" />
<option name="IGNORE_SPACES_IN_MERGE" value="false" />
<option name="CHECK_NESTED_FOR_QUICK_MERGE" value="false" />
<option name="IGNORE_SPACES_IN_ANNOTATE" value="true" />
<option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" />
<option name="FORCE_UPDATE" value="false" />
<option name="IGNORE_EXTERNALS" value="false" />
<configuration useDefault="false">$USER_HOME$/.subversion</configuration>
<myIsUseDefaultProxy>false</myIsUseDefaultProxy>
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/owncloud.iml" filepath="$PROJECT_DIR$/.idea/owncloud.iml" />
</modules>
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at the following url: |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Andrei Zmievski <andrei@php.net> |
// +----------------------------------------------------------------------+
//
// $Id: Getopt.php,v 1.21.4.7 2003/12/05 21:57:01 andrei Exp $
require_once( 'PEAR.php');
/**
* Command-line options parsing class.
*
* @author Andrei Zmievski <andrei@php.net>
*
*/
class Console_Getopt {
/**
* Parses the command-line options.
*
* The first parameter to this function should be the list of command-line
* arguments without the leading reference to the running program.
*
* The second parameter is a string of allowed short options. Each of the
* option letters can be followed by a colon ':' to specify that the option
* requires an argument, or a double colon '::' to specify that the option
* takes an optional argument.
*
* The third argument is an optional array of allowed long options. The
* leading '--' should not be included in the option name. Options that
* require an argument should be followed by '=', and options that take an
* option argument should be followed by '=='.
*
* The return value is an array of two elements: the list of parsed
* options and the list of non-option command-line arguments. Each entry in
* the list of parsed options is a pair of elements - the first one
* specifies the option, and the second one specifies the option argument,
* if there was one.
*
* Long and short options can be mixed.
*
* Most of the semantics of this function are based on GNU getopt_long().
*
* @param array $args an array of command-line arguments
* @param string $short_options specifies the list of allowed short options
* @param array $long_options specifies the list of allowed long options
*
* @return array two-element array containing the list of parsed options and
* the non-option arguments
*
* @access public
*
*/
function getopt2($args, $short_options, $long_options = null)
{
return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
}
/**
* This function expects $args to start with the script name (POSIX-style).
* Preserved for backwards compatibility.
* @see getopt2()
*/
function getopt($args, $short_options, $long_options = null)
{
return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
}
/**
* The actual implementation of the argument parsing code.
*/
function doGetopt($version, $args, $short_options, $long_options = null)
{
// in case you pass directly readPHPArgv() as the first arg
if (PEAR::isError($args)) {
return $args;
}
if (empty($args)) {
return array(array(), array());
}
$opts = array();
$non_opts = array();
settype($args, 'array');
if ($long_options) {
sort($long_options);
}
/*
* Preserve backwards compatibility with callers that relied on
* erroneous POSIX fix.
*/
if ($version < 2) {
if (isset($args[0]{0}) && $args[0]{0} != '-') {
array_shift($args);
}
}
reset($args);
while (list($i, $arg) = each($args)) {
/* The special element '--' means explicit end of
options. Treat the rest of the arguments as non-options
and end the loop. */
if ($arg == '--') {
$non_opts = array_merge($non_opts, array_slice($args, $i + 1));
break;
}
if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
$non_opts = array_merge($non_opts, array_slice($args, $i));
break;
} elseif (strlen($arg) > 1 && $arg{1} == '-') {
$error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
if (PEAR::isError($error))
return $error;
} else {
$error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
if (PEAR::isError($error))
return $error;
}
}
return array($opts, $non_opts);
}
/**
* @access private
*
*/
function _parseShortOption($arg, $short_options, &$opts, &$args)
{
for ($i = 0; $i < strlen($arg); $i++) {
$opt = $arg{$i};
$opt_arg = null;
/* Try to find the short option in the specifier string. */
if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
{
return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
}
if (strlen($spec) > 1 && $spec{1} == ':') {
if (strlen($spec) > 2 && $spec{2} == ':') {
if ($i + 1 < strlen($arg)) {
/* Option takes an optional argument. Use the remainder of
the arg string if there is anything left. */
$opts[] = array($opt, substr($arg, $i + 1));
break;
}
} else {
/* Option requires an argument. Use the remainder of the arg
string if there is anything left. */
if ($i + 1 < strlen($arg)) {
$opts[] = array($opt, substr($arg, $i + 1));
break;
} else if (list(, $opt_arg) = each($args))
/* Else use the next argument. */;
else
return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
}
}
$opts[] = array($opt, $opt_arg);
}
}
/**
* @access private
*
*/
function _parseLongOption($arg, $long_options, &$opts, &$args)
{
@list($opt, $opt_arg) = explode('=', $arg);
$opt_len = strlen($opt);
for ($i = 0; $i < count($long_options); $i++) {
$long_opt = $long_options[$i];
$opt_start = substr($long_opt, 0, $opt_len);
/* Option doesn't match. Go on to the next one. */
if ($opt_start != $opt)
continue;
$opt_rest = substr($long_opt, $opt_len);
/* Check that the options uniquely matches one of the allowed
options. */
if ($opt_rest != '' && $opt{0} != '=' &&
$i + 1 < count($long_options) &&
$opt == substr($long_options[$i+1], 0, $opt_len)) {
return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
}
if (substr($long_opt, -1) == '=') {
if (substr($long_opt, -2) != '==') {
/* Long option requires an argument.
Take the next argument if one wasn't specified. */;
if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
}
}
} else if ($opt_arg) {
return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
}
$opts[] = array('--' . $opt, $opt_arg);
return;
}
return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
}
/**
* Safely read the $argv PHP array across different PHP configurations.
* Will take care on register_globals and register_argc_argv ini directives
*
* @access public
* @return mixed the $argv PHP array or PEAR error if not registered
*/
function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if (!@is_array($_SERVER['argv'])) {
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}
}
?>
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Crypt_Blowfish allows for encryption and decryption on the fly using
* the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt
* PHP extension, it uses only PHP.
* Crypt_Blowfish support encryption/decryption with or without a secret key.
*
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Blowfish.php,v 1.81 2005/05/30 18:40:36 mfonda Exp $
* @link http://pear.php.net/package/Crypt_Blowfish
*/
require_once 'PEAR.php';
/**
*
* Example usage:
* $bf = new Crypt_Blowfish('some secret key!');
* $encrypted = $bf->encrypt('this is some example plain text');
* $plaintext = $bf->decrypt($encrypted);
* echo "plain text: $plaintext";
*
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/Crypt_Blowfish
* @version @package_version@
* @access public
*/
class Crypt_Blowfish
{
/**
* P-Array contains 18 32-bit subkeys
*
* @var array
* @access private
*/
var $_P = array();
/**
* Array of four S-Blocks each containing 256 32-bit entries
*
* @var array
* @access private
*/
var $_S = array();
/**
* Mcrypt td resource
*
* @var resource
* @access private
*/
var $_td = null;
/**
* Initialization vector
*
* @var string
* @access private
*/
var $_iv = null;
/**
* Crypt_Blowfish Constructor
* Initializes the Crypt_Blowfish object, and gives a sets
* the secret key
*
* @param string $key
* @access public
*/
function Crypt_Blowfish($key)
{
if (extension_loaded('mcrypt')) {
$this->_td = mcrypt_module_open(MCRYPT_BLOWFISH, '', 'ecb', '');
$this->_iv = mcrypt_create_iv(8, MCRYPT_RAND);
}
$this->setKey($key);
}
/**
* Deprecated isReady method
*
* @return bool
* @access public
* @deprecated
*/
function isReady()
{
return true;
}
/**
* Deprecated init method - init is now a private
* method and has been replaced with _init
*
* @return bool
* @access public
* @deprecated
* @see Crypt_Blowfish::_init()
*/
function init()
{
$this->_init();
}
/**
* Initializes the Crypt_Blowfish object
*
* @access private
*/
function _init()
{
$defaults = new Crypt_Blowfish_DefaultKey();
$this->_P = $defaults->P;
$this->_S = $defaults->S;
}
/**
* Enciphers a single 64 bit block
*
* @param int &$Xl
* @param int &$Xr
* @access private
*/
function _encipher(&$Xl, &$Xr)
{
for ($i = 0; $i < 16; $i++) {
$temp = $Xl ^ $this->_P[$i];
$Xl = ((($this->_S[0][($temp>>24) & 255] +
$this->_S[1][($temp>>16) & 255]) ^
$this->_S[2][($temp>>8) & 255]) +
$this->_S[3][$temp & 255]) ^ $Xr;
$Xr = $temp;
}
$Xr = $Xl ^ $this->_P[16];
$Xl = $temp ^ $this->_P[17];
}
/**
* Deciphers a single 64 bit block
*
* @param int &$Xl
* @param int &$Xr
* @access private
*/
function _decipher(&$Xl, &$Xr)
{
for ($i = 17; $i > 1; $i--) {
$temp = $Xl ^ $this->_P[$i];
$Xl = ((($this->_S[0][($temp>>24) & 255] +
$this->_S[1][($temp>>16) & 255]) ^
$this->_S[2][($temp>>8) & 255]) +
$this->_S[3][$temp & 255]) ^ $Xr;
$Xr = $temp;
}
$Xr = $Xl ^ $this->_P[1];
$Xl = $temp ^ $this->_P[0];
}
/**
* Encrypts a string
*
* @param string $plainText
* @return string Returns cipher text on success, PEAR_Error on failure
* @access public
*/
function encrypt($plainText)
{
if (!is_string($plainText)) {
PEAR::raiseError('Plain text must be a string', 0, PEAR_ERROR_DIE);
}
if (extension_loaded('mcrypt')) {
return mcrypt_generic($this->_td, $plainText);
}
$cipherText = '';
$len = strlen($plainText);
$plainText .= str_repeat(chr(0),(8 - ($len%8))%8);
for ($i = 0; $i < $len; $i += 8) {
list(,$Xl,$Xr) = unpack("N2",substr($plainText,$i,8));
$this->_encipher($Xl, $Xr);
$cipherText .= pack("N2", $Xl, $Xr);
}
return $cipherText;
}
/**
* Decrypts an encrypted string
*
* @param string $cipherText
* @return string Returns plain text on success, PEAR_Error on failure
* @access public
*/
function decrypt($cipherText)
{
if (!is_string($cipherText)) {
PEAR::raiseError('Chiper text must be a string', 1, PEAR_ERROR_DIE);
}
if (extension_loaded('mcrypt')) {
return mdecrypt_generic($this->_td, $cipherText);
}
$plainText = '';
$len = strlen($cipherText);
$cipherText .= str_repeat(chr(0),(8 - ($len%8))%8);
for ($i = 0; $i < $len; $i += 8) {
list(,$Xl,$Xr) = unpack("N2",substr($cipherText,$i,8));
$this->_decipher($Xl, $Xr);
$plainText .= pack("N2", $Xl, $Xr);
}
return $plainText;
}
/**
* Sets the secret key
* The key must be non-zero, and less than or equal to
* 56 characters in length.
*
* @param string $key
* @return bool Returns true on success, PEAR_Error on failure
* @access public
*/
function setKey($key)
{
if (!is_string($key)) {
PEAR::raiseError('Key must be a string', 2, PEAR_ERROR_DIE);
}
$len = strlen($key);
if ($len > 56 || $len == 0) {
PEAR::raiseError('Key must be less than 56 characters and non-zero. Supplied key length: ' . $len, 3, PEAR_ERROR_DIE);
}
if (extension_loaded('mcrypt')) {
mcrypt_generic_init($this->_td, $key, $this->_iv);
return true;
}
require_once 'Blowfish/DefaultKey.php';
$this->_init();
$k = 0;
$data = 0;
$datal = 0;
$datar = 0;
for ($i = 0; $i < 18; $i++) {
$data = 0;
for ($j = 4; $j > 0; $j--) {
$data = $data << 8 | ord($key{$k});
$k = ($k+1) % $len;
}
$this->_P[$i] ^= $data;
}
for ($i = 0; $i <= 16; $i += 2) {
$this->_encipher($datal, $datar);
$this->_P[$i] = $datal;
$this->_P[$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[0][$i] = $datal;
$this->_S[0][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[1][$i] = $datal;
$this->_S[1][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[2][$i] = $datal;
$this->_S[2][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[3][$i] = $datal;
$this->_S[3][$i+1] = $datar;
}
return true;
}
}
?>
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Crypt_Blowfish allows for encryption and decryption on the fly using
* the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt
* PHP extension, it uses only PHP.
* Crypt_Blowfish support encryption/decryption with or without a secret key.
*
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: DefaultKey.php,v 1.81 2005/05/30 18:40:37 mfonda Exp $
* @link http://pear.php.net/package/Crypt_Blowfish
*/
/**
* Class containing default key
*
* @category Encryption
* @package Crypt_Blowfish
* @author Matthew Fonda <mfonda@php.net>
* @copyright 2005 Matthew Fonda
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/Crypt_Blowfish
* @version @package_version@
* @access public
*/
class Crypt_Blowfish_DefaultKey
{
var $P = array();
var $S = array();
function Crypt_Blowfish_DefaultKey()
{
$this->P = array(
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
0x9216D5D9, 0x8979FB1B
);
$this->S = array(
array(
0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
),
array(
0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
),
array(
0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
),
array(
0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
)
);
}
}
?>
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: MDB2.php,v 1.335 2008/11/29 14:57:01 afz Exp $
//
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
require_once('PEAR.php');
// {{{ Error constants
/**
* The method mapErrorCode in each MDB2_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 MDB2::errorMessage().
*/
define('MDB2_OK', true);
define('MDB2_ERROR', -1);
define('MDB2_ERROR_SYNTAX', -2);
define('MDB2_ERROR_CONSTRAINT', -3);
define('MDB2_ERROR_NOT_FOUND', -4);
define('MDB2_ERROR_ALREADY_EXISTS', -5);
define('MDB2_ERROR_UNSUPPORTED', -6);
define('MDB2_ERROR_MISMATCH', -7);
define('MDB2_ERROR_INVALID', -8);
define('MDB2_ERROR_NOT_CAPABLE', -9);
define('MDB2_ERROR_TRUNCATED', -10);
define('MDB2_ERROR_INVALID_NUMBER', -11);
define('MDB2_ERROR_INVALID_DATE', -12);
define('MDB2_ERROR_DIVZERO', -13);
define('MDB2_ERROR_NODBSELECTED', -14);
define('MDB2_ERROR_CANNOT_CREATE', -15);
define('MDB2_ERROR_CANNOT_DELETE', -16);
define('MDB2_ERROR_CANNOT_DROP', -17);
define('MDB2_ERROR_NOSUCHTABLE', -18);
define('MDB2_ERROR_NOSUCHFIELD', -19);
define('MDB2_ERROR_NEED_MORE_DATA', -20);
define('MDB2_ERROR_NOT_LOCKED', -21);
define('MDB2_ERROR_VALUE_COUNT_ON_ROW', -22);
define('MDB2_ERROR_INVALID_DSN', -23);
define('MDB2_ERROR_CONNECT_FAILED', -24);
define('MDB2_ERROR_EXTENSION_NOT_FOUND',-25);
define('MDB2_ERROR_NOSUCHDB', -26);
define('MDB2_ERROR_ACCESS_VIOLATION', -27);
define('MDB2_ERROR_CANNOT_REPLACE', -28);
define('MDB2_ERROR_CONSTRAINT_NOT_NULL',-29);
define('MDB2_ERROR_DEADLOCK', -30);
define('MDB2_ERROR_CANNOT_ALTER', -31);
define('MDB2_ERROR_MANAGER', -32);
define('MDB2_ERROR_MANAGER_PARSE', -33);
define('MDB2_ERROR_LOADMODULE', -34);
define('MDB2_ERROR_INSUFFICIENT_DATA', -35);
define('MDB2_ERROR_NO_PERMISSION', -36);
define('MDB2_ERROR_DISCONNECT_FAILED', -37);
// }}}
// {{{ Verbose constants
/**
* These are just helper constants to more verbosely express parameters to prepare()
*/
define('MDB2_PREPARE_MANIP', false);
define('MDB2_PREPARE_RESULT', null);
// }}}
// {{{ Fetchmode constants
/**
* This is a special constant that tells MDB2 the user hasn't specified
* any particular get mode, so the default should be used.
*/
define('MDB2_FETCHMODE_DEFAULT', 0);
/**
* Column data indexed by numbers, ordered from 0 and up
*/
define('MDB2_FETCHMODE_ORDERED', 1);
/**
* Column data indexed by column names
*/
define('MDB2_FETCHMODE_ASSOC', 2);
/**
* Column data as object properties
*/
define('MDB2_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.
* MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays
* is the column name, and the second level the row number.
*/
define('MDB2_FETCHMODE_FLIPPED', 4);
// }}}
// {{{ Portability mode constants
/**
* Portability: turn off all portability features.
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_NONE', 0);
/**
* Portability: convert names of tables and fields to case defined in the
* "field_case" option when using the query*(), fetch*() and tableInfo() methods.
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_FIX_CASE', 1);
/**
* Portability: right trim the data output by query*() and fetch*().
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_RTRIM', 2);
/**
* Portability: force reporting the number of rows deleted.
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_DELETE_COUNT', 4);
/**
* Portability: not needed in MDB2 (just left here for compatibility to DB)
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_NUMROWS', 8);
/**
* Portability: makes certain error messages in certain drivers compatible
* with those from other DBMS's.
*
* + mysql, mysqli: change unique/primary key constraints
* MDB2_ERROR_ALREADY_EXISTS -> MDB2_ERROR_CONSTRAINT
*
* + odbc(access): MS's ODBC driver reports 'no such field' as code
* 07001, which means 'too few parameters.' When this option is on
* that code gets mapped to MDB2_ERROR_NOSUCHFIELD.
*
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_ERRORS', 16);
/**
* Portability: convert empty values to null strings in data output by
* query*() and fetch*().
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32);
/**
* Portability: removes database/table qualifiers from associative indexes
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64);
/**
* Portability: turn on all portability features.
* @see MDB2_Driver_Common::setOption()
*/
define('MDB2_PORTABILITY_ALL', 127);
// }}}
// {{{ Globals for class instance tracking
/**
* These are global variables that are used to track the various class instances
*/
$GLOBALS['_MDB2_databases'] = array();
$GLOBALS['_MDB2_dsninfo_default'] = array(
'phptype' => false,
'dbsyntax' => false,
'username' => false,
'password' => false,
'protocol' => false,
'hostspec' => false,
'port' => false,
'socket' => false,
'database' => false,
'mode' => false,
);
// }}}
// {{{ class MDB2
/**
* The main 'MDB2' 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 MDB2 is as follows (indentation means inheritance):
*
* MDB2 The main MDB2 class. This is simply a utility class
* with some 'static' methods for creating MDB2 objects as
* well as common utility functions for other MDB2 classes.
*
* MDB2_Driver_Common The base for each MDB2 implementation. Provides default
* | implementations (in OO lingo virtual methods) for
* | the actual DB implementations as well as a bunch of
* | query utility functions.
* |
* +-MDB2_Driver_mysql The MDB2 implementation for MySQL. Inherits MDB2_Driver_Common.
* When calling MDB2::factory or MDB2::connect for MySQL
* connections, the object returned is an instance of this
* class.
* +-MDB2_Driver_pgsql The MDB2 implementation for PostGreSQL. Inherits MDB2_Driver_Common.
* When calling MDB2::factory or MDB2::connect for PostGreSQL
* connections, the object returned is an instance of this
* class.
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2
{
// {{{ function setOptions(&$db, $options)
/**
* set option array in an exiting database object
*
* @param MDB2_Driver_Common MDB2 object
* @param array An associative array of option names and their values.
*
* @return mixed MDB2_OK or a PEAR Error object
*
* @access public
*/
static function setOptions(&$db, $options)
{
if (is_array($options)) {
foreach ($options as $option => $value) {
$test = $db->setOption($option, $value);
if (PEAR::isError($test)) {
return $test;
}
}
}
return MDB2_OK;
}
// }}}
// {{{ function classExists($classname)
/**
* Checks if a class exists without triggering __autoload
*
* @param string classname
*
* @return bool true success and false on error
* @static
* @access public
*/
static function classExists($classname)
{
if (version_compare(phpversion(), "5.0", ">=")) {
return class_exists($classname, false);
}
return class_exists($classname);
}
// }}}
// {{{ function loadClass($class_name, $debug)
/**
* Loads a PEAR class.
*
* @param string classname to load
* @param bool if errors should be suppressed
*
* @return mixed true success or PEAR_Error on failure
*
* @access public
*/
static function loadClass($class_name, $debug)
{
if (!MDB2::classExists($class_name)) {
$file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
if ($debug) {
$include = include_once($file_name);
} else {
$include = include_once($file_name);
}
if (!$include) {
if (!MDB2::fileExists($file_name)) {
$msg = "unable to find package '$class_name' file '$file_name'";
} else {
$msg = "unable to load class '$class_name' from file '$file_name'";
}
$err =MDB2::raiseErrorStatic(MDB2_ERROR_NOT_FOUND, null, null, $msg);
return $err;
}
}
return MDB2_OK;
}
// }}}
// {{{ function &factory($dsn, $options = false)
/**
* Create a new MDB2 object for the specified database type
*
* IMPORTANT: In order for MDB2 to work properly it is necessary that
* you make sure that you work with a reference of the original
* object instead of a copy (this is a PHP4 quirk).
*
* For example:
* $db =& MDB2::factory($dsn);
* ^^
* And not:
* $db = MDB2::factory($dsn);
*
* @param mixed 'data source name', see the MDB2::parseDSN
* method for a description of the dsn format.
* Can also be specified as an array of the
* format returned by MDB2::parseDSN.
* @param array An associative array of option names and
* their values.
*
* @return mixed a newly created MDB2 object, or false on error
*
* @access public
*/
static function factory($dsn, $options = false)
{
$dsninfo = MDB2::parseDSN($dsn);
if (empty($dsninfo['phptype'])) {
$err =MDB2::raiseErrorStatic(MDB2_ERROR_NOT_FOUND,
null, null, 'no RDBMS driver specified');
return $err;
}
$class_name = 'MDB2_Driver_'.$dsninfo['phptype'];
$debug = (!empty($options['debug']));
$err = MDB2::loadClass($class_name, $debug);
if (PEAR::isError($err)) {
return $err;
}
$db =new $class_name();
$db->setDSN($dsninfo);
$err = MDB2::setOptions($db, $options);
if (PEAR::isError($err)) {
return $err;
}
return $db;
}
// }}}
// {{{ function &connect($dsn, $options = false)
/**
* Create a new MDB2_Driver_* connection object and connect to the specified
* database
*
* IMPORTANT: In order for MDB2 to work properly it is necessary that
* you make sure that you work with a reference of the original
* object instead of a copy (this is a PHP4 quirk).
*
* For example:
* $db =& MDB2::connect($dsn);
* ^^
* And not:
* $db = MDB2::connect($dsn);
* ^^
*
* @param mixed $dsn 'data source name', see the MDB2::parseDSN
* method for a description of the dsn format.
* Can also be specified as an array of the
* format returned by MDB2::parseDSN.
* @param array $options An associative array of option names and
* their values.
*
* @return mixed a newly created MDB2 connection object, or a MDB2
* error object on error
*
* @access public
* @see MDB2::parseDSN
*/
function &connect($dsn, $options = false)
{
$db =MDB2::factory($dsn, $options);
if (PEAR::isError($db)) {
return $db;
}
$err = $db->connect();
if (PEAR::isError($err)) {
$dsn = $db->getDSN('string', 'xxx');
$db->disconnect();
$err->addUserInfo($dsn);
return $err;
}
return $db;
}
// }}}
// {{{ function &singleton($dsn = null, $options = false)
/**
* Returns a MDB2 connection with the requested DSN.
* A new MDB2 connection object is only created if no object with the
* requested DSN exists yet.
*
* IMPORTANT: In order for MDB2 to work properly it is necessary that
* you make sure that you work with a reference of the original
* object instead of a copy (this is a PHP4 quirk).
*
* For example:
* $db =& MDB2::singleton($dsn);
* ^^
* And not:
* $db = MDB2::singleton($dsn);
* ^^
*
* @param mixed 'data source name', see the MDB2::parseDSN
* method for a description of the dsn format.
* Can also be specified as an array of the
* format returned by MDB2::parseDSN.
* @param array An associative array of option names and
* their values.
*
* @return mixed a newly created MDB2 connection object, or a MDB2
* error object on error
*
* @access public
* @see MDB2::parseDSN
*/
function &singleton($dsn = null, $options = false)
{
if ($dsn) {
$dsninfo = MDB2::parseDSN($dsn);
$dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo);
$keys = array_keys($GLOBALS['_MDB2_databases']);
for ($i=0, $j=count($keys); $i<$j; ++$i) {
if (isset($GLOBALS['_MDB2_databases'][$keys[$i]])) {
$tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('array');
if (count(array_diff_assoc($tmp_dsn, $dsninfo)) == 0) {
MDB2::setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options);
return $GLOBALS['_MDB2_databases'][$keys[$i]];
}
}
}
} elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases'])) {
$db =$GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])];
return $db;
}
$db =MDB2::factory($dsn, $options);
return $db;
}
// }}}
// {{{ function areEquals()
/**
* It looks like there's a memory leak in array_diff() in PHP 5.1.x,
* so use this method instead.
* @see http://pear.php.net/bugs/bug.php?id=11790
*
* @param array $arr1
* @param array $arr2
* @return boolean
*/
static function areEquals($arr1, $arr2)
{
if (count($arr1) != count($arr2)) {
return false;
}
foreach (array_keys($arr1) as $k) {
if (!array_key_exists($k, $arr2) || $arr1[$k] != $arr2[$k]) {
return false;
}
}
return true;
}
// }}}
// {{{ function loadFile($file)
/**
* load a file (like 'Date')
*
* @param string name of the file in the MDB2 directory (without '.php')
*
* @return string name of the file that was included
*
* @access public
*/
function loadFile($file)
{
$file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php';
if (!MDB2::fileExists($file_name)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'unable to find: '.$file_name);
}
if (!include_once($file_name)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'unable to load driver class: '.$file_name);
}
return $file_name;
}
// }}}
// {{{ function apiVersion()
/**
* Return the MDB2 API version
*
* @return string the MDB2 API version number
*
* @access public
*/
function apiVersion()
{
return '2.5.0b2';
}
// }}}
// {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
/**
* This method is used to communicate an error and invoke error
* callbacks etc. Basically a wrapper for PEAR::raiseError
* without the message string.
*
* @param mixed int error code
*
* @param int error mode, see PEAR_Error docs
*
* @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the
* error level (E_USER_NOTICE etc). If error mode is
* PEAR_ERROR_CALLBACK, this is the callback function,
* either as a function name, or as an array of an
* object and method name. For other error modes this
* parameter is ignored.
*
* @param string Extra debug information. Defaults to the last
* query and native error code.
*
* @return PEAR_Error instance of a PEAR Error object
*
* @access private
* @see PEAR_Error
*/
function raiseError($code = null,
$mode = null,
$options = null,
$userinfo = null,
$dummy1 = null,
$dummy2 = null,
$dummy3 = false)
{
$err =PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
return $err;
}
static function raiseErrorStatic($code = null,
$mode = null,
$options = null,
$userinfo = null,
$dummy1 = null,
$dummy2 = null,
$dummy3 = false)
{
$pear=new PEAR();
$err =$pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
return $err;
}
// }}}
// {{{ function isError($data, $code = null)
/**
* Tell whether a value is a MDB2 error.
*
* @param mixed the value to test
* @param int if is an error object, return true
* only if $code is a string and
* $db->getMessage() == $code or
* $code is an integer and $db->getCode() == $code
*
* @return bool true if parameter is an error
*
* @access public
*/
static function isError($data, $code = null)
{
if ($data instanceof MDB2_Error) {
if (is_null($code)) {
return true;
} elseif (is_string($code)) {
return $data->getMessage() === $code;
} else {
$code = (array)$code;
return in_array($data->getCode(), $code);
}
}
return false;
}
// }}}
// {{{ function isConnection($value)
/**
* Tell whether a value is a MDB2 connection
*
* @param mixed value to test
*
* @return bool whether $value is a MDB2 connection
*
* @access public
*/
static function isConnection($value)
{
return ($value instanceof MDB2_Driver_Common);
}
// }}}
// {{{ function isResult($value)
/**
* Tell whether a value is a MDB2 result
*
* @param mixed value to test
*
* @return bool whether $value is a MDB2 result
*
* @access public
*/
static function isResult($value)
{
return $value instanceof MDB2_Result;
}
// }}}
// {{{ function isResultCommon($value)
/**
* Tell whether a value is a MDB2 result implementing the common interface
*
* @param mixed value to test
*
* @return bool whether $value is a MDB2 result implementing the common interface
*
* @access public
*/
static function isResultCommon($value)
{
return ($value instanceof MDB2_Result_Common);
}
// }}}
// {{{ function isStatement($value)
/**
* Tell whether a value is a MDB2 statement interface
*
* @param mixed value to test
*
* @return bool whether $value is a MDB2 statement interface
*
* @access public
*/
static function isStatement($value)
{
return $value instanceof MDB2_Statement_Common;
}
// }}}
// {{{ function errorMessage($value = null)
/**
* Return a textual error message for a MDB2 error code
*
* @param int|array integer error code,
null to get the current error code-message map,
or an array with a new error code-message map
*
* @return string error message, or false if the error code was
* not recognized
*
* @access public
*/
static function errorMessage($value = null)
{
static $errorMessages;
if (is_array($value)) {
$errorMessages = $value;
return MDB2_OK;
}
if (!isset($errorMessages)) {
$errorMessages = array(
MDB2_OK => 'no error',
MDB2_ERROR => 'unknown error',
MDB2_ERROR_ALREADY_EXISTS => 'already exists',
MDB2_ERROR_CANNOT_CREATE => 'can not create',
MDB2_ERROR_CANNOT_ALTER => 'can not alter',
MDB2_ERROR_CANNOT_REPLACE => 'can not replace',
MDB2_ERROR_CANNOT_DELETE => 'can not delete',
MDB2_ERROR_CANNOT_DROP => 'can not drop',
MDB2_ERROR_CONSTRAINT => 'constraint violation',
MDB2_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
MDB2_ERROR_DIVZERO => 'division by zero',
MDB2_ERROR_INVALID => 'invalid',
MDB2_ERROR_INVALID_DATE => 'invalid date or time',
MDB2_ERROR_INVALID_NUMBER => 'invalid number',
MDB2_ERROR_MISMATCH => 'mismatch',
MDB2_ERROR_NODBSELECTED => 'no database selected',
MDB2_ERROR_NOSUCHFIELD => 'no such field',
MDB2_ERROR_NOSUCHTABLE => 'no such table',
MDB2_ERROR_NOT_CAPABLE => 'MDB2 backend not capable',
MDB2_ERROR_NOT_FOUND => 'not found',
MDB2_ERROR_NOT_LOCKED => 'not locked',
MDB2_ERROR_SYNTAX => 'syntax error',
MDB2_ERROR_UNSUPPORTED => 'not supported',
MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
MDB2_ERROR_INVALID_DSN => 'invalid DSN',
MDB2_ERROR_CONNECT_FAILED => 'connect failed',
MDB2_ERROR_NEED_MORE_DATA => 'insufficient data supplied',
MDB2_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
MDB2_ERROR_NOSUCHDB => 'no such database',
MDB2_ERROR_ACCESS_VIOLATION => 'insufficient permissions',
MDB2_ERROR_LOADMODULE => 'error while including on demand module',
MDB2_ERROR_TRUNCATED => 'truncated',
MDB2_ERROR_DEADLOCK => 'deadlock detected',
MDB2_ERROR_NO_PERMISSION => 'no permission',
MDB2_ERROR_DISCONNECT_FAILED => 'disconnect failed',
);
}
if (is_null($value)) {
return $errorMessages;
}
if (PEAR::isError($value)) {
$value = $value->getCode();
}
return isset($errorMessages[$value]) ?
$errorMessages[$value] : $errorMessages[MDB2_ERROR];
}
// }}}
// {{{ function parseDSN($dsn)
/**
* Parse a data source name.
*
* Additional keys can be added by appending a URI query string to the
* end of the DSN.
*
* The format of the supplied DSN is in its fullest form:
* <code>
* phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
* </code>
*
* Most variations are allowed:
* <code>
* phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
* phptype://username:password@hostspec/database_name
* phptype://username:password@hostspec
* phptype://username@hostspec
* phptype://hostspec/database
* phptype://hostspec
* phptype(dbsyntax)
* phptype
* </code>
*
* @param string Data Source Name to be parsed
*
* @return array an associative array with the following keys:
* + 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
*
* @access public
* @author Tomas V.V.Cox <cox@idecnet.com>
*/
static function parseDSN($dsn)
{
$parsed = $GLOBALS['_MDB2_dsninfo_default'];
if (is_array($dsn)) {
$dsn = array_merge($parsed, $dsn);
if (!$dsn['dbsyntax']) {
$dsn['dbsyntax'] = $dsn['phptype'];
}
return $dsn;
}
// 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'] = !$arr[2] ? $arr[1] : $arr[2];
} else {
$parsed['phptype'] = $str;
$parsed['dbsyntax'] = $str;
}
if (!count($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 = $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, '//') === 0
&& strpos($dsn, '/', 2) !== false
&& $parsed['phptype'] == 'oci8'
) {
//oracle's "Easy Connect" syntax:
//"username/password@[//]host[:port][/service_name]"
//e.g. "scott/tiger@//mymachine:1521/oracle"
$proto_opts = $dsn;
$dsn = substr($proto_opts, strrpos($proto_opts, '/') + 1);
} elseif (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 (strpos($proto_opts, ':') !== false) {
list($proto_opts, $parsed['port']) = explode(':', $proto_opts);
}
if ($parsed['protocol'] == 'tcp') {
$parsed['hostspec'] = $proto_opts;
} elseif ($parsed['protocol'] == 'unix') {
$parsed['socket'] = $proto_opts;
}
// Get dabase if any
// $dsn => database
if ($dsn) {
// /database
if (($pos = strpos($dsn, '?')) === false) {
$parsed['database'] = $dsn;
// /database?param1=value1&param2=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;
}
// }}}
// {{{ function fileExists($file)
/**
* Checks if a file exists in the include path
*
* @param string filename
*
* @return bool true success and false on error
*
* @access public
*/
static function fileExists($file)
{
// safe_mode does notwork with is_readable()
if (!@ini_get('safe_mode')) {
$dirs = explode(PATH_SEPARATOR, ini_get('include_path'));
array_unshift($dirs,OC::$SERVERROOT);
array_unshift($dirs,OC::$SERVERROOT. DIRECTORY_SEPARATOR .'inc');
// print_r($dirs);die();
foreach ($dirs as $dir) {
if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
return true;
}
}
} else {
$fp = @fopen($file, 'r', true);
if (is_resource($fp)) {
@fclose($fp);
return true;
}
}
return false;
}
// }}}
}
// }}}
// {{{ class MDB2_Error extends PEAR_Error
/**
* MDB2_Error implements a class for reporting portable database error
* messages.
*
* @package MDB2
* @category Database
* @author Stig Bakken <ssb@fast.no>
*/
class MDB2_Error extends PEAR_Error
{
// {{{ constructor: function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null)
/**
* MDB2_Error constructor.
*
* @param mixed MDB2 error code, or string with error message.
* @param int what 'error mode' to operate in
* @param int what error level to use for $mode raPEAR_ERROR_TRIGGER
* @param mixed additional debug info, such as the last query
*/
function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null, $dummy = null)
{
if (is_null($code)) {
$code = MDB2_ERROR;
}
$this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code,
$mode, $level, $debuginfo);
}
// }}}
}
// }}}
// {{{ class MDB2_Driver_Common extends PEAR
/**
* MDB2_Driver_Common: Base class that is extended by each MDB2 driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Common extends PEAR
{
// {{{ Variables (Properties)
/**
* index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array
* @var int
* @access public
*/
var $db_index = 0;
/**
* DSN used for the next query
* @var array
* @access protected
*/
var $dsn = array();
/**
* DSN that was used to create the current connection
* @var array
* @access protected
*/
var $connected_dsn = array();
/**
* connection resource
* @var mixed
* @access protected
*/
var $connection = 0;
/**
* if the current opened connection is a persistent connection
* @var bool
* @access protected
*/
var $opened_persistent;
/**
* the name of the database for the next query
* @var string
* @access protected
*/
var $database_name = '';
/**
* the name of the database currently selected
* @var string
* @access protected
*/
var $connected_database_name = '';
/**
* server version information
* @var string
* @access protected
*/
var $connected_server_info = '';
/**
* list of all supported features of the given driver
* @var array
* @access public
*/
var $supported = array(
'sequences' => false,
'indexes' => false,
'affected_rows' => false,
'summary_functions' => false,
'order_by_text' => false,
'transactions' => false,
'savepoints' => false,
'current_id' => false,
'limit_queries' => false,
'LOBs' => false,
'replace' => false,
'sub_selects' => false,
'triggers' => false,
'auto_increment' => false,
'primary_key' => false,
'result_introspection' => false,
'prepared_statements' => false,
'identifier_quoting' => false,
'pattern_escaping' => false,
'new_link' => false,
);
/**
* Array of supported options that can be passed to the MDB2 instance.
*
* The options can be set during object creation, using
* MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can
* also be set after the object is created, using MDB2::setOptions() or
* MDB2_Driver_Common::setOption().
* The list of available option includes:
* <ul>
* <li>$options['ssl'] -> boolean: determines if ssl should be used for connections</li>
* <li>$options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what case to force on field/table names</li>
* <li>$options['disable_query'] -> boolean: determines if queries should be executed</li>
* <li>$options['result_class'] -> string: class used for result sets</li>
* <li>$options['buffered_result_class'] -> string: class used for buffered result sets</li>
* <li>$options['result_wrap_class'] -> string: class used to wrap result sets into</li>
* <li>$options['result_buffering'] -> boolean should results be buffered or not?</li>
* <li>$options['fetch_class'] -> string: class to use when fetch mode object is used</li>
* <li>$options['persistent'] -> boolean: persistent connection?</li>
* <li>$options['debug'] -> integer: numeric debug level</li>
* <li>$options['debug_handler'] -> string: function/method that captures debug messages</li>
* <li>$options['debug_expanded_output'] -> bool: BC option to determine if more context information should be send to the debug handler</li>
* <li>$options['default_text_field_length'] -> integer: default text field length to use</li>
* <li>$options['lob_buffer_length'] -> integer: LOB buffer length</li>
* <li>$options['log_line_break'] -> string: line-break format</li>
* <li>$options['idxname_format'] -> string: pattern for index name</li>
* <li>$options['seqname_format'] -> string: pattern for sequence name</li>
* <li>$options['savepoint_format'] -> string: pattern for auto generated savepoint names</li>
* <li>$options['statement_format'] -> string: pattern for prepared statement names</li>
* <li>$options['seqcol_name'] -> string: sequence column name</li>
* <li>$options['quote_identifier'] -> boolean: if identifier quoting should be done when check_option is used</li>
* <li>$options['use_transactions'] -> boolean: if transaction use should be enabled</li>
* <li>$options['decimal_places'] -> integer: number of decimal places to handle</li>
* <li>$options['portability'] -> integer: portability constant</li>
* <li>$options['modules'] -> array: short to long module name mapping for __call()</li>
* <li>$options['emulate_prepared'] -> boolean: force prepared statements to be emulated</li>
* <li>$options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes</li>
* <li>$options['datatype_map_callback'] -> array: callback function/method that should be called</li>
* <li>$options['bindname_format'] -> string: regular expression pattern for named parameters</li>
* <li>$options['multi_query'] -> boolean: determines if queries returning multiple result sets should be executed</li>
* <li>$options['max_identifiers_length'] -> integer: max identifier length</li>
* <li>$options['default_fk_action_onupdate'] -> string: default FOREIGN KEY ON UPDATE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li>
* <li>$options['default_fk_action_ondelete'] -> string: default FOREIGN KEY ON DELETE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li>
* </ul>
*
* @var array
* @access public
* @see MDB2::connect()
* @see MDB2::factory()
* @see MDB2::singleton()
* @see MDB2_Driver_Common::setOption()
*/
var $options = array(
'ssl' => false,
'field_case' => CASE_LOWER,
'disable_query' => false,
'result_class' => 'MDB2_Result_%s',
'buffered_result_class' => 'MDB2_BufferedResult_%s',
'result_wrap_class' => false,
'result_buffering' => true,
'fetch_class' => 'stdClass',
'persistent' => false,
'debug' => 0,
'debug_handler' => 'MDB2_defaultDebugOutput',
'debug_expanded_output' => false,
'default_text_field_length' => 4096,
'lob_buffer_length' => 8192,
'log_line_break' => "\n",
'idxname_format' => '%s_idx',
'seqname_format' => '%s_seq',
'savepoint_format' => 'MDB2_SAVEPOINT_%s',
'statement_format' => 'MDB2_STATEMENT_%1$s_%2$s',
'seqcol_name' => 'sequence',
'quote_identifier' => false,
'use_transactions' => true,
'decimal_places' => 2,
'portability' => MDB2_PORTABILITY_ALL,
'modules' => array(
'ex' => 'Extended',
'dt' => 'Datatype',
'mg' => 'Manager',
'rv' => 'Reverse',
'na' => 'Native',
'fc' => 'Function',
),
'emulate_prepared' => false,
'datatype_map' => array(),
'datatype_map_callback' => array(),
'nativetype_map_callback' => array(),
'lob_allow_url_include' => false,
'bindname_format' => '(?:\d+)|(?:[a-zA-Z][a-zA-Z0-9_]*)',
'max_identifiers_length' => 30,
'default_fk_action_onupdate' => 'RESTRICT',
'default_fk_action_ondelete' => 'RESTRICT',
);
/**
* string array
* @var string
* @access protected
*/
var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false);
/**
* identifier quoting
* @var array
* @access protected
*/
var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
/**
* sql comments
* @var array
* @access protected
*/
var $sql_comments = array(
array('start' => '--', 'end' => "\n", 'escape' => false),
array('start' => '/*', 'end' => '*/', 'escape' => false),
);
/**
* comparision wildcards
* @var array
* @access protected
*/
var $wildcards = array('%', '_');
/**
* column alias keyword
* @var string
* @access protected
*/
var $as_keyword = ' AS ';
/**
* warnings
* @var array
* @access protected
*/
var $warnings = array();
/**
* string with the debugging information
* @var string
* @access public
*/
var $debug_output = '';
/**
* determine if there is an open transaction
* @var bool
* @access protected
*/
var $in_transaction = false;
/**
* the smart transaction nesting depth
* @var int
* @access protected
*/
var $nested_transaction_counter = null;
/**
* the first error that occured inside a nested transaction
* @var MDB2_Error|bool
* @access protected
*/
var $has_transaction_error = false;
/**
* result offset used in the next query
* @var int
* @access protected
*/
var $offset = 0;
/**
* result limit used in the next query
* @var int
* @access protected
*/
var $limit = 0;
/**
* Database backend used in PHP (mysql, odbc etc.)
* @var string
* @access public
*/
var $phptype;
/**
* Database used with regards to SQL syntax etc.
* @var string
* @access public
*/
var $dbsyntax;
/**
* the last query sent to the driver
* @var string
* @access public
*/
var $last_query;
/**
* the default fetchmode used
* @var int
* @access protected
*/
var $fetchmode = MDB2_FETCHMODE_ORDERED;
/**
* array of module instances
* @var array
* @access protected
*/
var $modules = array();
/**
* determines of the PHP4 destructor emulation has been enabled yet
* @var array
* @access protected
*/
var $destructor_registered = true;
// }}}
// {{{ constructor: function __construct()
/**
* Constructor
*/
function __construct()
{
end($GLOBALS['_MDB2_databases']);
$db_index = key($GLOBALS['_MDB2_databases']) + 1;
$GLOBALS['_MDB2_databases'][$db_index] = &$this;
$this->db_index = $db_index;
}
// }}}
// {{{ destructor: function __destruct()
/**
* Destructor
*/
function __destruct()
{
$this->disconnect(false);
}
// }}}
// {{{ function free()
/**
* Free the internal references so that the instance can be destroyed
*
* @return bool true on success, false if result is invalid
*
* @access public
*/
function free()
{
unset($GLOBALS['_MDB2_databases'][$this->db_index]);
unset($this->db_index);
return MDB2_OK;
}
// }}}
// {{{ function __toString()
/**
* String conversation
*
* @return string representation of the object
*
* @access public
*/
function __toString()
{
$info = get_class($this);
$info.= ': (phptype = '.$this->phptype.', dbsyntax = '.$this->dbsyntax.')';
if ($this->connection) {
$info.= ' [connected]';
}
return $info;
}
// }}}
// {{{ function errorInfo($error = null)
/**
* This method is used to collect information about an error
*
* @param mixed error code or resource
*
* @return array with MDB2 errorcode, native error code, native message
*
* @access public
*/
function errorInfo($error = null)
{
return array($error, null, null);
}
// }}}
// {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
/**
* This method is used to communicate an error and invoke error
* callbacks etc. Basically a wrapper for PEAR::raiseError
* without the message string.
*
* @param mixed $code integer error code, or a PEAR error object (all
* other parameters are ignored if this parameter is
* an object
* @param int $mode error mode, see PEAR_Error docs
* @param mixed $options If error mode is PEAR_ERROR_TRIGGER, this is the
* error level (E_USER_NOTICE etc). If error mode is
* PEAR_ERROR_CALLBACK, this is the callback function,
* either as a function name, or as an array of an
* object and method name. For other error modes this
* parameter is ignored.
* @param string $userinfo Extra debug information. Defaults to the last
* query and native error code.
* @param string $method name of the method that triggered the error
* @param string $dummy1 not used
* @param bool $dummy2 not used
*
* @return PEAR_Error instance of a PEAR Error object
* @access public
* @see PEAR_Error
*/
function raiseError($code = null,
$mode = null,
$options = null,
$userinfo = null,
$method = null,
$dummy1 = null,
$dummy2 = false
) {
$userinfo = "[Error message: $userinfo]\n";
// The error is yet a MDB2 error object
if (PEAR::isError($code)) {
// because we use the static PEAR::raiseError, our global
// handler should be used if it is set
if (is_null($mode) && !empty($this->_default_error_mode)) {
$mode = $this->_default_error_mode;
$options = $this->_default_error_options;
}
if (is_null($userinfo)) {
$userinfo = $code->getUserinfo();
}
$code = $code->getCode();
} elseif ($code == MDB2_ERROR_NOT_FOUND) {
// extension not loaded: don't call $this->errorInfo() or the script
// will die
} elseif (isset($this->connection)) {
if (!empty($this->last_query)) {
$userinfo.= "[Last executed query: {$this->last_query}]\n";
}
$native_errno = $native_msg = null;
list($code, $native_errno, $native_msg) = $this->errorInfo($code);
if (!is_null($native_errno) && $native_errno !== '') {
$userinfo.= "[Native code: $native_errno]\n";
}
if (!is_null($native_msg) && $native_msg !== '') {
$userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n";
}
echo $userinfo;
if (!is_null($method)) {
$userinfo = $method.': '.$userinfo;
}
}
$err = PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
if ($err->getMode() !== PEAR_ERROR_RETURN
&& isset($this->nested_transaction_counter) && !$this->has_transaction_error) {
$this->has_transaction_error =$err;
}
return $err;
}
// }}}
// {{{ function resetWarnings()
/**
* reset the warning array
*
* @return void
*
* @access public
*/
function resetWarnings()
{
$this->warnings = array();
}
// }}}
// {{{ function getWarnings()
/**
* Get all warnings in reverse order.
* This means that the last warning is the first element in the array
*
* @return array with warnings
*
* @access public
* @see resetWarnings()
*/
function getWarnings()
{
return array_reverse($this->warnings);
}
// }}}
// {{{ function setFetchMode($fetchmode, $object_class = 'stdClass')
/**
* Sets which fetch mode should be used by default on queries
* on this connection
*
* @param int MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC
* or MDB2_FETCHMODE_OBJECT
* @param string the class name of the object to be returned
* by the fetch methods when the
* MDB2_FETCHMODE_OBJECT mode is selected.
* If no class is specified by default a cast
* to object from the assoc array row will be
* done. There is also the possibility to use
* and extend the 'MDB2_row' class.
*
* @return mixed MDB2_OK or MDB2 Error Object
*
* @access public
* @see MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJECT
*/
function setFetchMode($fetchmode, $object_class = 'stdClass')
{
switch ($fetchmode) {
case MDB2_FETCHMODE_OBJECT:
$this->options['fetch_class'] = $object_class;
case MDB2_FETCHMODE_ORDERED:
case MDB2_FETCHMODE_ASSOC:
$this->fetchmode = $fetchmode;
break;
default:
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'invalid fetchmode mode', __FUNCTION__);
}
return MDB2_OK;
}
// }}}
// {{{ function setOption($option, $value)
/**
* set the option for the db class
*
* @param string option name
* @param mixed value for the option
*
* @return mixed MDB2_OK or MDB2 Error Object
*
* @access public
*/
function setOption($option, $value)
{
if (array_key_exists($option, $this->options)) {
$this->options[$option] = $value;
return MDB2_OK;
}
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
"unknown option $option", __FUNCTION__);
}
// }}}
// {{{ function getOption($option)
/**
* Returns the value of an option
*
* @param string option name
*
* @return mixed the option value or error object
*
* @access public
*/
function getOption($option)
{
if (array_key_exists($option, $this->options)) {
return $this->options[$option];
}
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
"unknown option $option", __FUNCTION__);
}
// }}}
// {{{ function debug($message, $scope = '', $is_manip = null)
/**
* set a debug message
*
* @param string message that should be appended to the debug variable
* @param string usually the method name that triggered the debug call:
* for example 'query', 'prepare', 'execute', 'parameters',
* 'beginTransaction', 'commit', 'rollback'
* @param array contains context information about the debug() call
* common keys are: is_manip, time, result etc.
*
* @return void
*
* @access public
*/
function debug($message, $scope = '', $context = array())
{
if ($this->options['debug'] && $this->options['debug_handler']) {
if (!$this->options['debug_expanded_output']) {
if (!empty($context['when']) && $context['when'] !== 'pre') {
return null;
}
$context = empty($context['is_manip']) ? false : $context['is_manip'];
}
return call_user_func_array($this->options['debug_handler'], array(&$this, $scope, $message, $context));
}
return null;
}
// }}}
// {{{ function getDebugOutput()
/**
* output debug info
*
* @return string content of the debug_output class variable
*
* @access public
*/
function getDebugOutput()
{
return $this->debug_output;
}
// }}}
// {{{ function escape($text)
/**
* Quotes a string so it can be safely used in a query. It will quote
* the text so it can safely be used within a query.
*
* @param string the input string to quote
* @param bool escape wildcards
*
* @return string quoted string
*
* @access public
*/
function escape($text, $escape_wildcards = false)
{
if ($escape_wildcards) {
$text = $this->escapePattern($text);
}
$text = str_replace($this->string_quoting['end'], $this->string_quoting['escape'] . $this->string_quoting['end'], $text);
return $text;
}
// }}}
// {{{ function escapePattern($text)
/**
* Quotes pattern (% and _) characters in a string)
*
* @param string the input string to quote
*
* @return string quoted string
*
* @access public
*/
function escapePattern($text)
{
if ($this->string_quoting['escape_pattern']) {
$text = str_replace($this->string_quoting['escape_pattern'], $this->string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $text);
foreach ($this->wildcards as $wildcard) {
$text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $wildcard, $text);
}
}
return $text;
}
// }}}
// {{{ function quoteIdentifier($str, $check_option = false)
/**
* Quote a string so it can be safely used as a table or column name
*
* Delimiting style depends on which database driver is being used.
*
* NOTE: just because you CAN use delimited identifiers doesn't mean
* you SHOULD use them. In general, they end up causing way more
* problems than they solve.
*
* NOTE: if you have table names containing periods, don't use this method
* (@see bug #11906)
*
* Portability is broken by using the following characters inside
* delimited identifiers:
* + backtick (<kbd>`</kbd>) -- due to MySQL
* + double quote (<kbd>"</kbd>) -- due to Oracle
* + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
*
* Delimited identifiers are known to generally work correctly under
* the following drivers:
* + mssql
* + mysql
* + mysqli
* + oci8
* + pgsql
* + sqlite
*
* InterBase doesn't seem to be able to use delimited identifiers
* via PHP 4. They work fine under PHP 5.
*
* @param string identifier name to be quoted
* @param bool check the 'quote_identifier' option
*
* @return string quoted identifier string
*
* @access public
*/
function quoteIdentifier($str, $check_option = false)
{
if ($check_option && !$this->options['quote_identifier']) {
return $str;
}
$str = str_replace($this->identifier_quoting['end'], $this->identifier_quoting['escape'] . $this->identifier_quoting['end'], $str);
$parts = explode('.', $str);
foreach (array_keys($parts) as $k) {
$parts[$k] = $this->identifier_quoting['start'] . $parts[$k] . $this->identifier_quoting['end'];
}
return implode('.', $parts);
}
// }}}
// {{{ function getAsKeyword()
/**
* Gets the string to alias column
*
* @return string to use when aliasing a column
*/
function getAsKeyword()
{
return $this->as_keyword;
}
// }}}
// {{{ function getConnection()
/**
* Returns a native connection
*
* @return mixed a valid MDB2 connection object,
* or a MDB2 error object on error
*
* @access public
*/
function getConnection()
{
$result = $this->connect();
if (PEAR::isError($result)) {
return $result;
}
return $this->connection;
}
// }}}
// {{{ function _fixResultArrayValues(&$row, $mode)
/**
* Do all necessary conversions on result arrays to fix DBMS quirks
*
* @param array the array to be fixed (passed by reference)
* @param array bit-wise addition of the required portability modes
*
* @return void
*
* @access protected
*/
function _fixResultArrayValues(&$row, $mode)
{
switch ($mode) {
case MDB2_PORTABILITY_EMPTY_TO_NULL:
foreach ($row as $key => $value) {
if ($value === '') {
$row[$key] = null;
}
}
break;
case MDB2_PORTABILITY_RTRIM:
foreach ($row as $key => $value) {
if (is_string($value)) {
$row[$key] = rtrim($value);
}
}
break;
case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES:
$tmp_row = array();
foreach ($row as $key => $value) {
$tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
}
$row = $tmp_row;
break;
case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL):
foreach ($row as $key => $value) {
if ($value === '') {
$row[$key] = null;
} elseif (is_string($value)) {
$row[$key] = rtrim($value);
}
}
break;
case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
$tmp_row = array();
foreach ($row as $key => $value) {
if (is_string($value)) {
$value = rtrim($value);
}
$tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
}
$row = $tmp_row;
break;
case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
$tmp_row = array();
foreach ($row as $key => $value) {
if ($value === '') {
$value = null;
}
$tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
}
$row = $tmp_row;
break;
case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
$tmp_row = array();
foreach ($row as $key => $value) {
if ($value === '') {
$value = null;
} elseif (is_string($value)) {
$value = rtrim($value);
}
$tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
}
$row = $tmp_row;
break;
}
}
// }}}
// {{{ function &loadModule($module, $property = null, $phptype_specific = null)
/**
* loads a module
*
* @param string name of the module that should be loaded
* (only used for error messages)
* @param string name of the property into which the class will be loaded
* @param bool if the class to load for the module is specific to the
* phptype
*
* @return object on success a reference to the given module is returned
* and on failure a PEAR error
*
* @access public
*/
function &loadModule($module, $property = null, $phptype_specific = null)
{
if (!$property) {
$property = strtolower($module);
}
if (!isset($this->{$property})) {
$version = $phptype_specific;
if ($phptype_specific !== false) {
$version = true;
$class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype;
$file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
}
if ($phptype_specific === false
|| (!MDB2::classExists($class_name) && !MDB2::fileExists($file_name))
) {
$version = false;
$class_name = 'MDB2_'.$module;
$file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
}
$err = MDB2::loadClass($class_name, $this->getOption('debug'));
if (PEAR::isError($err)) {
return $err;
}
// load module in a specific version
if ($version) {
if (method_exists($class_name, 'getClassName')) {
$class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index);
if ($class_name != $class_name_new) {
$class_name = $class_name_new;
$err = MDB2::loadClass($class_name, $this->getOption('debug'));
if (PEAR::isError($err)) {
return $err;
}
}
}
}
if (!MDB2::classExists($class_name)) {
$err =$this->raiseError(MDB2_ERROR_LOADMODULE, null, null,
"unable to load module '$module' into property '$property'", __FUNCTION__);
return $err;
}
$this->{$property} = new $class_name($this->db_index);
$this->modules[$module] =$this->{$property};
if ($version) {
// this will be used in the connect method to determine if the module
// needs to be loaded with a different version if the server
// version changed in between connects
$this->loaded_version_modules[] = $property;
}
}
return $this->{$property};
}
// }}}
// {{{ function __call($method, $params)
/**
* Calls a module method using the __call magic method
*
* @param string Method name.
* @param array Arguments.
*
* @return mixed Returned value.
*/
function __call($method, $params)
{
$module = null;
if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match)
&& isset($this->options['modules'][$match[1]])
) {
$module = $this->options['modules'][$match[1]];
$method = strtolower($match[2]).$match[3];
if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) {
$result =& $this->loadModule($module);
if (PEAR::isError($result)) {
return $result;
}
}
} else {
foreach ($this->modules as $key => $foo) {
if (is_object($this->modules[$key])
&& method_exists($this->modules[$key], $method)
) {
$module = $key;
break;
}
}
}
if (!is_null($module)) {
return call_user_func_array(array(&$this->modules[$module], $method), $params);
}
trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR);
}
// }}}
// {{{ function beginTransaction($savepoint = null)
/**
* Start a transaction or set a savepoint.
*
* @param string name of a savepoint to set
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function beginTransaction($savepoint = null)
{
$this->debug('Starting transaction', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'transactions are not supported', __FUNCTION__);
}
// }}}
// {{{ function commit($savepoint = null)
/**
* Commit the database changes done during a transaction that is in
* progress or release a savepoint. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after committing the pending changes.
*
* @param string name of a savepoint to release
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function commit($savepoint = null)
{
$this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'commiting transactions is not supported', __FUNCTION__);
}
// }}}
// {{{ function rollback($savepoint = null)
/**
* Cancel any database changes done during a transaction or since a specific
* savepoint that is in progress. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after canceling the pending changes.
*
* @param string name of a savepoint to rollback to
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function rollback($savepoint = null)
{
$this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'rolling back transactions is not supported', __FUNCTION__);
}
// }}}
// {{{ function inTransaction($ignore_nested = false)
/**
* If a transaction is currently open.
*
* @param bool if the nested transaction count should be ignored
* @return int|bool - an integer with the nesting depth is returned if a
* nested transaction is open
* - true is returned for a normal open transaction
* - false is returned if no transaction is open
*
* @access public
*/
function inTransaction($ignore_nested = false)
{
if (!$ignore_nested && isset($this->nested_transaction_counter)) {
return $this->nested_transaction_counter;
}
return $this->in_transaction;
}
// }}}
// {{{ function setTransactionIsolation($isolation)
/**
* Set the transacton isolation level.
*
* @param string standard isolation level
* READ UNCOMMITTED (allows dirty reads)
* READ COMMITTED (prevents dirty reads)
* REPEATABLE READ (prevents nonrepeatable reads)
* SERIALIZABLE (prevents phantom reads)
* @param array some transaction options:
* 'wait' => 'WAIT' | 'NO WAIT'
* 'rw' => 'READ WRITE' | 'READ ONLY'
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
* @since 2.1.1
*/
static function setTransactionIsolation($isolation, $options = array())
{
$this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'isolation level setting is not supported', __FUNCTION__);
}
// }}}
// {{{ function beginNestedTransaction($savepoint = false)
/**
* Start a nested transaction.
*
* @return mixed MDB2_OK on success/savepoint name, a MDB2 error on failure
*
* @access public
* @since 2.1.1
*/
function beginNestedTransaction()
{
if ($this->in_transaction) {
++$this->nested_transaction_counter;
$savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
if ($this->supports('savepoints') && $savepoint) {
return $this->beginTransaction($savepoint);
}
return MDB2_OK;
}
$this->has_transaction_error = false;
$result = $this->beginTransaction();
$this->nested_transaction_counter = 1;
return $result;
}
// }}}
// {{{ function completeNestedTransaction($force_rollback = false, $release = false)
/**
* Finish a nested transaction by rolling back if an error occured or
* committing otherwise.
*
* @param bool if the transaction should be rolled back regardless
* even if no error was set within the nested transaction
* @return mixed MDB_OK on commit/counter decrementing, false on rollback
* and a MDB2 error on failure
*
* @access public
* @since 2.1.1
*/
function completeNestedTransaction($force_rollback = false)
{
if ($this->nested_transaction_counter > 1) {
$savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
if ($this->supports('savepoints') && $savepoint) {
if ($force_rollback || $this->has_transaction_error) {
$result = $this->rollback($savepoint);
if (!PEAR::isError($result)) {
$result = false;
$this->has_transaction_error = false;
}
} else {
$result = $this->commit($savepoint);
}
} else {
$result = MDB2_OK;
}
--$this->nested_transaction_counter;
return $result;
}
$this->nested_transaction_counter = null;
$result = MDB2_OK;
// transaction has not yet been rolled back
if ($this->in_transaction) {
if ($force_rollback || $this->has_transaction_error) {
$result = $this->rollback();
if (!PEAR::isError($result)) {
$result = false;
}
} else {
$result = $this->commit();
}
}
$this->has_transaction_error = false;
return $result;
}
// }}}
// {{{ function failNestedTransaction($error = null, $immediately = false)
/**
* Force setting nested transaction to failed.
*
* @param mixed value to return in getNestededTransactionError()
* @param bool if the transaction should be rolled back immediately
* @return bool MDB2_OK
*
* @access public
* @since 2.1.1
*/
function failNestedTransaction($error = null, $immediately = false)
{
if (is_null($error)) {
$error = $this->has_transaction_error ? $this->has_transaction_error : true;
} elseif (!$error) {
$error = true;
}
$this->has_transaction_error = $error;
if (!$immediately) {
return MDB2_OK;
}
return $this->rollback();
}
// }}}
// {{{ function getNestedTransactionError()
/**
* The first error that occured since the transaction start.
*
* @return MDB2_Error|bool MDB2 error object if an error occured or false.
*
* @access public
* @since 2.1.1
*/
function getNestedTransactionError()
{
return $this->has_transaction_error;
}
// }}}
// {{{ connect()
/**
* Connect to the database
*
* @return true on success, MDB2 Error Object on failure
*/
function connect()
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ databaseExists()
/**
* check if given database name is exists?
*
* @param string $name name of the database that should be checked
*
* @return mixed true/false on success, a MDB2 error on failure
* @access public
*/
function databaseExists($name)
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ setCharset($charset, $connection = null)
/**
* Set the charset on the current connection
*
* @param string charset
* @param resource connection handle
*
* @return true on success, MDB2 Error Object on failure
*/
function setCharset($charset, $connection = null)
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function disconnect($force = true)
/**
* Log out and disconnect from the database.
*
* @param boolean $force whether the disconnect should be forced even if the
* connection is opened persistently
*
* @return mixed true on success, false if not connected and error object on error
*
* @access public
*/
function disconnect($force = true)
{
$this->connection = 0;
$this->connected_dsn = array();
$this->connected_database_name = '';
$this->opened_persistent = null;
$this->connected_server_info = '';
$this->in_transaction = null;
$this->nested_transaction_counter = null;
return MDB2_OK;
}
// }}}
// {{{ function setDatabase($name)
/**
* Select a different database
*
* @param string name of the database that should be selected
*
* @return string name of the database previously connected to
*
* @access public
*/
function setDatabase($name)
{
$previous_database_name = (isset($this->database_name)) ? $this->database_name : '';
$this->database_name = $name;
if (!empty($this->connected_database_name) && ($this->connected_database_name != $this->database_name)) {
$this->disconnect(false);
}
return $previous_database_name;
}
// }}}
// {{{ function getDatabase()
/**
* Get the current database
*
* @return string name of the database
*
* @access public
*/
function getDatabase()
{
return $this->database_name;
}
// }}}
// {{{ function setDSN($dsn)
/**
* set the DSN
*
* @param mixed DSN string or array
*
* @return MDB2_OK
*
* @access public
*/
function setDSN($dsn)
{
$dsn_default = $GLOBALS['_MDB2_dsninfo_default'];
$dsn = MDB2::parseDSN($dsn);
if (array_key_exists('database', $dsn)) {
$this->database_name = $dsn['database'];
unset($dsn['database']);
}
$this->dsn = array_merge($dsn_default, $dsn);
return $this->disconnect(false);
}
// }}}
// {{{ function getDSN($type = 'string', $hidepw = false)
/**
* return the DSN as a string
*
* @param string format to return ("array", "string")
* @param string string to hide the password with
*
* @return mixed DSN in the chosen type
*
* @access public
*/
function getDSN($type = 'string', $hidepw = false)
{
$dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn);
$dsn['phptype'] = $this->phptype;
$dsn['database'] = $this->database_name;
if ($hidepw) {
$dsn['password'] = $hidepw;
}
switch ($type) {
// expand to include all possible options
case 'string':
$dsn = $dsn['phptype'].
($dsn['dbsyntax'] ? ('('.$dsn['dbsyntax'].')') : '').
'://'.$dsn['username'].':'.
$dsn['password'].'@'.$dsn['hostspec'].
($dsn['port'] ? (':'.$dsn['port']) : '').
'/'.$dsn['database'];
break;
case 'array':
default:
break;
}
return $dsn;
}
// }}}
// {{{ _isNewLinkSet()
/**
* Check if the 'new_link' option is set
*
* @return boolean
*
* @access protected
*/
function _isNewLinkSet()
{
return (isset($this->dsn['new_link'])
&& ($this->dsn['new_link'] === true
|| (is_string($this->dsn['new_link']) && preg_match('/^true$/i', $this->dsn['new_link']))
|| (is_numeric($this->dsn['new_link']) && 0 != (int)$this->dsn['new_link'])
)
);
}
// }}}
// {{{ function &standaloneQuery($query, $types = null, $is_manip = false)
/**
* execute a query as database administrator
*
* @param string the SQL query
* @param mixed array that contains the types of the columns in
* the result set
* @param bool if the query is a manipulation query
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function &standaloneQuery($query, $types = null, $is_manip = false)
{
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$result =$this->_doQuery($query, $is_manip, $connection, false);
if (PEAR::isError($result)) {
return $result;
}
if ($is_manip) {
$affected_rows = $this->_affectedRows($connection, $result);
return $affected_rows;
}
$result =$this->_wrapResult($result, $types, true, false, $limit, $offset);
return $result;
}
// }}}
// {{{ function _modifyQuery($query, $is_manip, $limit, $offset)
/**
* Changes a query string for various DBMS specific reasons
*
* @param string query to modify
* @param bool if it is a DML query
* @param int limit the number of rows
* @param int start reading from given offset
*
* @return string modified query
*
* @access protected
*/
function _modifyQuery($query, $is_manip, $limit, $offset)
{
return $query;
}
// }}}
// {{{ function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
/**
* Execute a query
* @param string query
* @param bool if the query is a manipulation query
* @param resource connection handle
* @param string database name
*
* @return result or error object
*
* @access protected
*/
function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
{
$this->last_query = $query;
$result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (PEAR::isError($result)) {
return $result;
}
$query = $result;
}
$err =$this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $err;
}
// }}}
// {{{ function _affectedRows($connection, $result = null)
/**
* Returns the number of rows affected
*
* @param resource result handle
* @param resource connection handle
*
* @return mixed MDB2 Error Object or the number of rows affected
*
* @access private
*/
function _affectedRows($connection, $result = null)
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function &exec($query)
/**
* Execute a manipulation query to the database and return the number of affected rows
*
* @param string the SQL query
*
* @return mixed number of affected rows on success, a MDB2 error on failure
*
* @access public
*/
function &exec($query)
{
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$query = $this->_modifyQuery($query, true, $limit, $offset);
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$result =$this->_doQuery($query, true, $connection, $this->database_name);
if (PEAR::isError($result)) {
return $result;
}
$affectedRows = $this->_affectedRows($connection, $result);
return $affectedRows;
}
// }}}
// {{{ function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
/**
* Send a query to the database and return any results
*
* @param string the SQL query
* @param mixed array that contains the types of the columns in
* the result set
* @param mixed string which specifies which result class to use
* @param mixed string which specifies which class to wrap results in
*
* @return mixed an MDB2_Result handle on success, a MDB2 error on failure
*
* @access public
*/
function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
{
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$query = $this->_modifyQuery($query, false, $limit, $offset);
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$result =$this->_doQuery($query, false, $connection, $this->database_name);
if (PEAR::isError($result)) {
return $result;
}
$result =$this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset);
return $result;
}
// }}}
// {{{ function &_wrapResult($result, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null)
/**
* wrap a result set into the correct class
*
* @param resource result handle
* @param mixed array that contains the types of the columns in
* the result set
* @param mixed string which specifies which result class to use
* @param mixed string which specifies which class to wrap results in
* @param string number of rows to select
* @param string first row to select
*
* @return mixed an MDB2_Result, a MDB2 error on failure
*
* @access protected
*/
function &_wrapResult($result, $types = array(), $result_class = true,
$result_wrap_class = false, $limit = null, $offset = null)
{
if ($types === true) {
if ($this->supports('result_introspection')) {
$this->loadModule('Reverse', null, true);
$tableInfo = $this->reverse->tableInfo($result);
if (PEAR::isError($tableInfo)) {
return $tableInfo;
}
$types = array();
foreach ($tableInfo as $field) {
$types[] = $field['mdb2type'];
}
} else {
$types = null;
}
}
if ($result_class === true) {
$result_class = $this->options['result_buffering']
? $this->options['buffered_result_class'] : $this->options['result_class'];
}
if ($result_class) {
$class_name = sprintf($result_class, $this->phptype);
if (!MDB2::classExists($class_name)) {
$err =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'result class does not exist '.$class_name, __FUNCTION__);
return $err;
}
$result =new $class_name($this, $result, $limit, $offset);
if (!MDB2::isResultCommon($result)) {
$err =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'result class is not extended from MDB2_Result_Common', __FUNCTION__);
return $err;
}
if (!empty($types)) {
$err = $result->setResultTypes($types);
if (PEAR::isError($err)) {
$result->free();
return $err;
}
}
}
if ($result_wrap_class === true) {
$result_wrap_class = $this->options['result_wrap_class'];
}
if ($result_wrap_class) {
if (!MDB2::classExists($result_wrap_class)) {
$err =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'result wrap class does not exist '.$result_wrap_class, __FUNCTION__);
return $err;
}
$result = new $result_wrap_class($result, $this->fetchmode);
}
return $result;
}
// }}}
// {{{ function getServerVersion($native = false)
/**
* return version information about the server
*
* @param bool determines if the raw version string should be returned
*
* @return mixed array with version information or row string
*
* @access public
*/
function getServerVersion($native = false)
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function setLimit($limit, $offset = null)
/**
* set the range of the next query
*
* @param string number of rows to select
* @param string first row to select
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function setLimit($limit, $offset = null)
{
if (!$this->supports('limit_queries')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'limit is not supported by this driver', __FUNCTION__);
}
$limit = (int)$limit;
if ($limit < 0) {
return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'it was not specified a valid selected range row limit', __FUNCTION__);
}
$this->limit = $limit;
if (!is_null($offset)) {
$offset = (int)$offset;
if ($offset < 0) {
return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'it was not specified a valid first selected range row', __FUNCTION__);
}
$this->offset = $offset;
}
return MDB2_OK;
}
// }}}
// {{{ function subSelect($query, $type = false)
/**
* simple subselect emulation: leaves the query untouched for all RDBMS
* that support subselects
*
* @param string the SQL query for the subselect that may only
* return a column
* @param string determines type of the field
*
* @return string the query
*
* @access public
*/
function subSelect($query, $type = false)
{
if ($this->supports('sub_selects') === true) {
return $query;
}
if (!$this->supports('sub_selects')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
$col = $this->queryCol($query, $type);
if (PEAR::isError($col)) {
return $col;
}
if (!is_array($col) || count($col) == 0) {
return 'NULL';
}
if ($type) {
$this->loadModule('Datatype', null, true);
return $this->datatype->implodeArray($col, $type);
}
return implode(', ', $col);
}
// }}}
// {{{ function replace($table, $fields)
/**
* Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
* query, except that if there is already a row in the table with the same
* key field values, the old row is deleted before the new row is inserted.
*
* The REPLACE type of query does not make part of the SQL standards. Since
* practically only MySQL and SQLite implement it natively, this type of
* query isemulated through this method for other DBMS using standard types
* of queries inside a transaction to assure the atomicity of the operation.
*
* @param string name of the table on which the REPLACE query will
* be executed.
* @param array associative array that describes the fields and the
* values that will be inserted or updated in the specified table. The
* indexes of the array are the names of all the fields of the table.
* The values of the array are also associative arrays that describe
* the values and other properties of the table fields.
*
* Here follows a list of field properties that need to be specified:
*
* value
* Value to be assigned to the specified field. This value may be
* of specified in database independent type format as this
* function can perform the necessary datatype conversions.
*
* Default: this property is required unless the Null property is
* set to 1.
*
* type
* Name of the type of the field. Currently, all types MDB2
* are supported except for clob and blob.
*
* Default: no type conversion
*
* null
* bool property that indicates that the value for this field
* should be set to null.
*
* The default value for fields missing in INSERT queries may be
* specified the definition of a table. Often, the default value
* is already null, but since the REPLACE may be emulated using
* an UPDATE query, make sure that all fields of the table are
* listed in this function argument array.
*
* Default: 0
*
* key
* bool property that indicates that this field should be
* handled as a primary key or at least as part of the compound
* unique index of the table that will determine the row that will
* updated if it exists or inserted a new row otherwise.
*
* This function will fail if no key field is specified or if the
* value of a key field is set to null because fields that are
* part of unique index they may not be null.
*
* Default: 0
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function replace($table, $fields)
{
if (!$this->supports('replace')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'replace query is not supported', __FUNCTION__);
}
$count = count($fields);
$condition = $values = array();
for ($colnum = 0, reset($fields); $colnum < $count; next($fields), $colnum++) {
$name = key($fields);
if (isset($fields[$name]['null']) && $fields[$name]['null']) {
$value = 'NULL';
} else {
$type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
$value = $this->quote($fields[$name]['value'], $type);
}
$values[$name] = $value;
if (isset($fields[$name]['key']) && $fields[$name]['key']) {
if ($value === 'NULL') {
return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
'key value '.$name.' may not be NULL', __FUNCTION__);
}
$condition[] = $this->quoteIdentifier($name, true) . '=' . $value;
}
}
if (empty($condition)) {
return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
'not specified which fields are keys', __FUNCTION__);
}
$result = null;
$in_transaction = $this->in_transaction;
if (!$in_transaction && PEAR::isError($result = $this->beginTransaction())) {
return $result;
}
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$condition = ' WHERE '.implode(' AND ', $condition);
$query = 'DELETE FROM ' . $this->quoteIdentifier($table, true) . $condition;
$result =$this->_doQuery($query, true, $connection);
if (!PEAR::isError($result)) {
$affected_rows = $this->_affectedRows($connection, $result);
$insert = '';
foreach ($values as $key => $value) {
$insert .= ($insert?', ':'') . $this->quoteIdentifier($key, true);
}
$values = implode(', ', $values);
$query = 'INSERT INTO '. $this->quoteIdentifier($table, true) . "($insert) VALUES ($values)";
$result =$this->_doQuery($query, true, $connection);
if (!PEAR::isError($result)) {
$affected_rows += $this->_affectedRows($connection, $result);;
}
}
if (!$in_transaction) {
if (PEAR::isError($result)) {
$this->rollback();
} else {
$result = $this->commit();
}
}
if (PEAR::isError($result)) {
return $result;
}
return $affected_rows;
}
// }}}
// {{{ function &prepare($query, $types = null, $result_types = null, $lobs = array())
/**
* Prepares a query for multiple execution with execute().
* With some database backends, this is emulated.
* prepare() requires a generic query as string like
* 'INSERT INTO numbers VALUES(?,?)' or
* 'INSERT INTO numbers VALUES(:foo,:bar)'.
* The ? and :name and are placeholders which can be set using
* bindParam() and the query can be sent off using the execute() method.
* The allowed format for :name can be set with the 'bindname_format' option.
*
* @param string the query to prepare
* @param mixed array that contains the types of the placeholders
* @param mixed array that contains the types of the columns in
* the result set or MDB2_PREPARE_RESULT, if set to
* MDB2_PREPARE_MANIP the query is handled as a manipulation query
* @param mixed key (field) value (parameter) pair for all lob placeholders
*
* @return mixed resource handle for the prepared query on success,
* a MDB2 error on failure
*
* @access public
* @see bindParam, execute
*/
function &prepare($query, $types = null, $result_types = null, $lobs = array())
{
$is_manip = ($result_types === MDB2_PREPARE_MANIP);
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (PEAR::isError($result)) {
return $result;
}
$query = $result;
}
$placeholder_type_guess = $placeholder_type = null;
$question = '?';
$colon = ':';
$positions = array();
$position = 0;
while ($position < strlen($query)) {
$q_position = strpos($query, $question, $position);
$c_position = strpos($query, $colon, $position);
if ($q_position && $c_position) {
$p_position = min($q_position, $c_position);
} elseif ($q_position) {
$p_position = $q_position;
} elseif ($c_position) {
$p_position = $c_position;
} else {
break;
}
if (is_null($placeholder_type)) {
$placeholder_type_guess = $query[$p_position];
}
$new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
if (PEAR::isError($new_pos)) {
return $new_pos;
}
if ($new_pos != $position) {
$position = $new_pos;
continue; //evaluate again starting from the new position
}
if ($query[$position] == $placeholder_type_guess) {
if (is_null($placeholder_type)) {
$placeholder_type = $query[$p_position];
$question = $colon = $placeholder_type;
if (!empty($types) && is_array($types)) {
if ($placeholder_type == ':') {
if (is_int(key($types))) {
$types_tmp = $types;
$types = array();
$count = -1;
}
} else {
$types = array_values($types);
}
}
}
if ($placeholder_type == ':') {
$regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s';
$parameter = preg_replace($regexp, '\\1', $query);
if ($parameter === '') {
$err =$this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'named parameter name must match "bindname_format" option', __FUNCTION__);
return $err;
}
$positions[$p_position] = $parameter;
$query = substr_replace($query, '?', $position, strlen($parameter)+1);
// use parameter name in type array
if (isset($count) && isset($types_tmp[++$count])) {
$types[$parameter] = $types_tmp[$count];
}
} else {
$positions[$p_position] = count($positions);
}
$position = $p_position + 1;
} else {
$position = $p_position;
}
}
$class_name = 'MDB2_Statement_'.$this->phptype;
$statement = null;
$obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
$this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
return $obj;
}
// }}}
// {{{ function _skipDelimitedStrings($query, $position, $p_position)
/**
* Utility method, used by prepare() to avoid replacing placeholders within delimited strings.
* Check if the placeholder is contained within a delimited string.
* If so, skip it and advance the position, otherwise return the current position,
* which is valid
*
* @param string $query
* @param integer $position current string cursor position
* @param integer $p_position placeholder position
*
* @return mixed integer $new_position on success
* MDB2_Error on failure
*
* @access protected
*/
function _skipDelimitedStrings($query, $position, $p_position)
{
$ignores = $this->string_quoting;
$ignores[] = $this->identifier_quoting;
$ignores = array_merge($ignores, $this->sql_comments);
foreach ($ignores as $ignore) {
if (!empty($ignore['start'])) {
if (is_int($start_quote = strpos($query, $ignore['start'], $position)) && $start_quote < $p_position) {
$end_quote = $start_quote;
do {
if (!is_int($end_quote = strpos($query, $ignore['end'], $end_quote + 1))) {
if ($ignore['end'] === "\n") {
$end_quote = strlen($query) - 1;
} else {
$err =$this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'query with an unterminated text string specified', __FUNCTION__);
return $err;
}
}
} while ($ignore['escape']
&& $end_quote-1 != $start_quote
&& $query[($end_quote - 1)] == $ignore['escape']
&& ( $ignore['escape_pattern'] !== $ignore['escape']
|| $query[($end_quote - 2)] != $ignore['escape'])
);
$position = $end_quote + 1;
return $position;
}
}
}
return $position;
}
// }}}
// {{{ function quote($value, $type = null, $quote = true)
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string text string value that is intended to be converted.
* @param string type to which the value should be converted to
* @param bool quote
* @param bool escape wildcards
*
* @return string text string that represents the given argument value in
* a DBMS specific format.
*
* @access public
*/
function quote($value, $type = null, $quote = true, $escape_wildcards = false)
{
$result = $this->loadModule('Datatype', null, true);
if (PEAR::isError($result)) {
return $result;
}
return $this->datatype->quote($value, $type, $quote, $escape_wildcards);
}
// }}}
// {{{ function getDeclaration($type, $name, $field)
/**
* Obtain DBMS specific SQL code portion needed to declare
* of the given type
*
* @param string type to which the value should be converted to
* @param string name the field to be declared.
* @param string definition of the field
*
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
*
* @access public
*/
function getDeclaration($type, $name, $field)
{
$result = $this->loadModule('Datatype', null, true);
if (PEAR::isError($result)) {
return $result;
}
return $this->datatype->getDeclaration($type, $name, $field);
}
// }}}
// {{{ function compareDefinition($current, $previous)
/**
* Obtain an array of changes that may need to applied
*
* @param array new definition
* @param array old definition
*
* @return array containing all changes that will need to be applied
*
* @access public
*/
function compareDefinition($current, $previous)
{
$result = $this->loadModule('Datatype', null, true);
if (PEAR::isError($result)) {
return $result;
}
return $this->datatype->compareDefinition($current, $previous);
}
// }}}
// {{{ function supports($feature)
/**
* Tell whether a DB implementation or its backend extension
* supports a given feature.
*
* @param string name of the feature (see the MDB2 class doc)
*
* @return bool|string if this DB implementation supports a given feature
* false means no, true means native,
* 'emulated' means emulated
*
* @access public
*/
function supports($feature)
{
if (array_key_exists($feature, $this->supported)) {
return $this->supported[$feature];
}
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
"unknown support feature $feature", __FUNCTION__);
}
// }}}
// {{{ function getSequenceName($sqn)
/**
* adds sequence name formatting to a sequence name
*
* @param string name of the sequence
*
* @return string formatted sequence name
*
* @access public
*/
function getSequenceName($sqn)
{
return sprintf($this->options['seqname_format'],
preg_replace('/[^a-z0-9_\-\$.]/i', '_', $sqn));
}
// }}}
// {{{ function getIndexName($idx)
/**
* adds index name formatting to a index name
*
* @param string name of the index
*
* @return string formatted index name
*
* @access public
*/
function getIndexName($idx)
{
return sprintf($this->options['idxname_format'],
preg_replace('/[^a-z0-9_\-\$.]/i', '_', $idx));
}
// }}}
// {{{ function nextID($seq_name, $ondemand = true)
/**
* Returns the next free id of a sequence
*
* @param string name of the sequence
* @param bool when true missing sequences are automatic created
*
* @return mixed MDB2 Error Object or id
*
* @access public
*/
function nextID($seq_name, $ondemand = true)
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function lastInsertID($table = null, $field = null)
/**
* Returns the autoincrement ID if supported or $id or fetches the current
* ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
*
* @param string name of the table into which a new row was inserted
* @param string name of the field into which a new row was inserted
*
* @return mixed MDB2 Error Object or id
*
* @access public
*/
function lastInsertID($table = null, $field = null)
{
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function currID($seq_name)
/**
* Returns the current id of a sequence
*
* @param string name of the sequence
*
* @return mixed MDB2 Error Object or id
*
* @access public
*/
function currID($seq_name)
{
$this->warnings[] = 'database does not support getting current
sequence value, the sequence value was incremented';
return $this->nextID($seq_name);
}
// }}}
// {{{ function queryOne($query, $type = null, $colnum = 0)
/**
* Execute the specified query, fetch the value from the first column of
* the first row of the result set and then frees
* the result set.
*
* @param string $query the SELECT query statement to be executed.
* @param string $type optional argument that specifies the expected
* datatype of the result set field, so that an eventual
* conversion may be performed. The default datatype is
* text, meaning that no conversion is performed
* @param mixed $colnum the column number (or name) to fetch
*
* @return mixed MDB2_OK or field value on success, a MDB2 error on failure
*
* @access public
*/
function queryOne($query, $type = null, $colnum = 0)
{
$result = $this->query($query, $type);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$one = $result->fetchOne($colnum);
$result->free();
return $one;
}
// }}}
// {{{ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
/**
* Execute the specified query, fetch the values from the first
* row of the result set into an array and then frees
* the result set.
*
* @param string the SELECT query statement to be executed.
* @param array optional array argument that specifies a list of
* expected datatypes of the result set columns, so that the eventual
* conversions may be performed. The default list of datatypes is
* empty, meaning that no conversion is performed.
* @param int how the array data should be indexed
*
* @return mixed MDB2_OK or data array on success, a MDB2 error on failure
*
* @access public
*/
function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
{
$result = $this->query($query, $types);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$row = $result->fetchRow($fetchmode);
$result->free();
return $row;
}
// }}}
// {{{ function queryCol($query, $type = null, $colnum = 0)
/**
* Execute the specified query, fetch the value from the first column of
* each row of the result set into an array and then frees the result set.
*
* @param string $query the SELECT query statement to be executed.
* @param string $type optional argument that specifies the expected
* datatype of the result set field, so that an eventual
* conversion may be performed. The default datatype is text,
* meaning that no conversion is performed
* @param mixed $colnum the column number (or name) to fetch
*
* @return mixed MDB2_OK or data array on success, a MDB2 error on failure
* @access public
*/
function queryCol($query, $type = null, $colnum = 0)
{
$result = $this->query($query, $type);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$col = $result->fetchCol($colnum);
$result->free();
return $col;
}
// }}}
// {{{ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
/**
* Execute the specified query, fetch all the rows of the result set into
* a two dimensional array and then frees the result set.
*
* @param string the SELECT query statement to be executed.
* @param array optional array argument that specifies a list of
* expected datatypes of the result set columns, so that the eventual
* conversions may be performed. The default list of datatypes is
* empty, meaning that no conversion is performed.
* @param int how the array data should be indexed
* @param bool if set to true, the $all will have the first
* column as its first dimension
* @param bool used only when the query returns exactly
* two columns. If true, the values of the returned array will be
* one-element arrays instead of scalars.
* @param bool if true, the values of the returned array is
* wrapped in another array. If the same key value (in the first
* column) repeats itself, the values will be appended to this array
* instead of overwriting the existing values.
*
* @return mixed MDB2_OK or data array on success, a MDB2 error on failure
*
* @access public
*/
function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
$rekey = false, $force_array = false, $group = false)
{
$result = $this->query($query, $types);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
$result->free();
return $all;
}
// }}}
}
// }}}
// {{{ class MDB2_Result
/**
* The dummy class that all user space result classes should extend from
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Result
{
}
// }}}
// {{{ class MDB2_Result_Common extends MDB2_Result
/**
* The common result class for MDB2 result objects
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Result_Common extends MDB2_Result
{
// {{{ Variables (Properties)
var $db;
var $result;
var $rownum = -1;
var $types = array();
var $values = array();
var $offset;
var $offset_count = 0;
var $limit;
var $column_names;
// }}}
// {{{ constructor: function __construct(&$db, &$result, $limit = 0, $offset = 0)
/**
* Constructor
*/
function __construct(&$db, &$result, $limit = 0, $offset = 0)
{
$this->db =$db;
$this->result =$result;
$this->offset = $offset;
$this->limit = max(0, $limit - 1);
}
// }}}
// {{{ function setResultTypes($types)
/**
* Define the list of types to be associated with the columns of a given
* result set.
*
* This function may be called before invoking fetchRow(), fetchOne(),
* fetchCol() and fetchAll() so that the necessary data type
* conversions are performed on the data to be retrieved by them. If this
* function is not called, the type of all result set columns is assumed
* to be text, thus leading to not perform any conversions.
*
* @param array variable that lists the
* data types to be expected in the result set columns. If this array
* contains less types than the number of columns that are returned
* in the result set, the remaining columns are assumed to be of the
* type text. Currently, the types clob and blob are not fully
* supported.
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function setResultTypes($types)
{
$load = $this->db->loadModule('Datatype', null, true);
if (PEAR::isError($load)) {
return $load;
}
$types = $this->db->datatype->checkResultTypes($types);
if (PEAR::isError($types)) {
return $types;
}
$this->types = $types;
return MDB2_OK;
}
// }}}
// {{{ function seek($rownum = 0)
/**
* Seek to a specific row in a result set
*
* @param int number of the row where the data can be found
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function seek($rownum = 0)
{
$target_rownum = $rownum - 1;
if ($this->rownum > $target_rownum) {
return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'seeking to previous rows not implemented', __FUNCTION__);
}
while ($this->rownum < $target_rownum) {
$this->fetchRow();
}
return MDB2_OK;
}
// }}}
// {{{ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
/**
* Fetch and return a row of data
*
* @param int how the array data should be indexed
* @param int number of the row where the data can be found
*
* @return int data array on success, a MDB2 error on failure
*
* @access public
*/
function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
{
$err =$this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $err;
}
// }}}
// {{{ function fetchOne($colnum = 0)
/**
* fetch single column from the next row from a result set
*
* @param int|string the column number (or name) to fetch
* @param int number of the row where the data can be found
*
* @return string data on success, a MDB2 error on failure
* @access public
*/
function fetchOne($colnum = 0, $rownum = null)
{
$fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
$row = $this->fetchRow($fetchmode, $rownum);
if (!is_array($row) || PEAR::isError($row)) {
return $row;
}
if (!array_key_exists($colnum, $row)) {
return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
'column is not defined in the result set: '.$colnum, __FUNCTION__);
}
return $row[$colnum];
}
// }}}
// {{{ function fetchCol($colnum = 0)
/**
* Fetch and return a column from the current row pointer position
*
* @param int|string the column number (or name) to fetch
*
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function fetchCol($colnum = 0)
{
$column = array();
$fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
$row = $this->fetchRow($fetchmode);
if (is_array($row)) {
if (!array_key_exists($colnum, $row)) {
return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
'column is not defined in the result set: '.$colnum, __FUNCTION__);
}
do {
$column[] = $row[$colnum];
} while (is_array($row = $this->fetchRow($fetchmode)));
}
if (PEAR::isError($row)) {
return $row;
}
return $column;
}
// }}}
// {{{ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
/**
* Fetch and return all rows from the current row pointer position
*
* @param int $fetchmode the fetch mode to use:
* + MDB2_FETCHMODE_ORDERED
* + MDB2_FETCHMODE_ASSOC
* + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIPPED
* + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPED
* @param bool if set to true, the $all will have the first
* column as its first dimension
* @param bool used only when the query returns exactly
* two columns. If true, the values of the returned array will be
* one-element arrays instead of scalars.
* @param bool if true, the values of the returned array is
* wrapped in another array. If the same key value (in the first
* column) repeats itself, the values will be appended to this array
* instead of overwriting the existing values.
*
* @return mixed data array on success, a MDB2 error on failure
*
* @access public
* @see getAssoc()
*/
function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false,
$force_array = false, $group = false)
{
$all = array();
$row = $this->fetchRow($fetchmode);
if (PEAR::isError($row)) {
return $row;
} elseif (!$row) {
return $all;
}
$shift_array = $rekey ? false : null;
if (!is_null($shift_array)) {
if (is_object($row)) {
$colnum = count(get_object_vars($row));
} else {
$colnum = count($row);
}
if ($colnum < 2) {
return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
'rekey feature requires atleast 2 column', __FUNCTION__);
}
$shift_array = (!$force_array && $colnum == 2);
}
if ($rekey) {
do {
if (is_object($row)) {
$arr = get_object_vars($row);
$key = reset($arr);
unset($row->{$key});
} else {
if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
$key = reset($row);
unset($row[key($row)]);
} else {
$key = array_shift($row);
}
if ($shift_array) {
$row = array_shift($row);
}
}
if ($group) {
$all[$key][] = $row;
} else {
$all[$key] = $row;
}
} while (($row = $this->fetchRow($fetchmode)));
} elseif ($fetchmode & MDB2_FETCHMODE_FLIPPED) {
do {
foreach ($row as $key => $val) {
$all[$key][] = $val;
}
} while (($row = $this->fetchRow($fetchmode)));
} else {
do {
$all[] = $row;
} while (($row = $this->fetchRow($fetchmode)));
}
return $all;
}
// }}}
// {{{ function rowCount()
/**
* Returns the actual row number that was last fetched (count from 0)
* @return int
*
* @access public
*/
function rowCount()
{
return $this->rownum + 1;
}
// }}}
// {{{ function numRows()
/**
* Returns the number of rows in a result object
*
* @return mixed MDB2 Error Object or the number of rows
*
* @access public
*/
function numRows()
{
return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function nextResult()
/**
* Move the internal result pointer to the next available result
*
* @return true on success, false if there is no more result set or an error object on failure
*
* @access public
*/
function nextResult()
{
return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function getColumnNames()
/**
* Retrieve the names of columns returned by the DBMS in a query result or
* from the cache.
*
* @param bool If set to true the values are the column names,
* otherwise the names of the columns are the keys.
* @return mixed Array variable that holds the names of columns or an
* MDB2 error on failure.
* Some DBMS may not return any columns when the result set
* does not contain any rows.
*
* @access public
*/
function getColumnNames($flip = false)
{
if (!isset($this->column_names)) {
$result = $this->_getColumnNames();
if (PEAR::isError($result)) {
return $result;
}
$this->column_names = $result;
}
if ($flip) {
return array_flip($this->column_names);
}
return $this->column_names;
}
// }}}
// {{{ function _getColumnNames()
/**
* Retrieve the names of columns returned by the DBMS in a query result.
*
* @return mixed Array variable that holds the names of columns as keys
* or an MDB2 error on failure.
* Some DBMS may not return any columns when the result set
* does not contain any rows.
*
* @access private
*/
function _getColumnNames()
{
return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function numCols()
/**
* Count the number of columns returned by the DBMS in a query result.
*
* @return mixed integer value with the number of columns, a MDB2 error
* on failure
*
* @access public
*/
function numCols()
{
return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ function getResource()
/**
* return the resource associated with the result object
*
* @return resource
*
* @access public
*/
function getResource()
{
return $this->result;
}
// }}}
// {{{ function bindColumn($column, &$value, $type = null)
/**
* Set bind variable to a column.
*
* @param int column number or name
* @param mixed variable reference
* @param string specifies the type of the field
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function bindColumn($column, &$value, $type = null)
{
if (!is_numeric($column)) {
$column_names = $this->getColumnNames();
if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($this->db->options['field_case'] == CASE_LOWER) {
$column = strtolower($column);
} else {
$column = strtoupper($column);
}
}
$column = $column_names[$column];
}
$this->values[$column] =$value;
if (!is_null($type)) {
$this->types[$column] = $type;
}
return MDB2_OK;
}
// }}}
// {{{ function _assignBindColumns($row)
/**
* Bind a variable to a value in the result row.
*
* @param array row data
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access private
*/
function _assignBindColumns($row)
{
$row = array_values($row);
foreach ($row as $column => $value) {
if (array_key_exists($column, $this->values)) {
$this->values[$column] = $value;
}
}
return MDB2_OK;
}
// }}}
// {{{ function free()
/**
* Free the internal resources associated with result.
*
* @return bool true on success, false if result is invalid
*
* @access public
*/
function free()
{
$this->result = false;
return MDB2_OK;
}
// }}}
}
// }}}
// {{{ class MDB2_Row
/**
* The simple class that accepts row data as an array
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Row
{
// {{{ constructor: function __construct(&$row)
/**
* constructor
*
* @param resource row data as array
*/
function __construct(&$row)
{
foreach ($row as $key => $value) {
$this->$key = &$row[$key];
}
}
}
// }}}
// {{{ class MDB2_Statement_Common
/**
* The common statement class for MDB2 statement objects
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Statement_Common
{
// {{{ Variables (Properties)
var $db;
var $statement;
var $query;
var $result_types;
var $types;
var $values = array();
var $limit;
var $offset;
var $is_manip;
// }}}
// {{{ constructor: function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
/**
* Constructor
*/
function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
{
$this->db =$db;
$this->statement =$statement;
$this->positions = $positions;
$this->query = $query;
$this->types = (array)$types;
$this->result_types = (array)$result_types;
$this->limit = $limit;
$this->is_manip = $is_manip;
$this->offset = $offset;
}
// }}}
// {{{ function bindValue($parameter, &$value, $type = null)
/**
* Set the value of a parameter of a prepared query.
*
* @param int the order number of the parameter in the query
* statement. The order number of the first parameter is 1.
* @param mixed value that is meant to be assigned to specified
* parameter. The type of the value depends on the $type argument.
* @param string specifies the type of the field
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function bindValue($parameter, $value, $type = null)
{
if (!is_numeric($parameter)) {
$parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
}
if (!in_array($parameter, $this->positions)) {
return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
}
$this->values[$parameter] = $value;
if (!is_null($type)) {
$this->types[$parameter] = $type;
}
return MDB2_OK;
}
// }}}
// {{{ function bindValueArray($values, $types = null)
/**
* Set the values of multiple a parameter of a prepared query in bulk.
*
* @param array specifies all necessary information
* for bindValue() the array elements must use keys corresponding to
* the number of the position of the parameter.
* @param array specifies the types of the fields
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
* @see bindParam()
*/
function bindValueArray($values, $types = null)
{
$types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
$parameters = array_keys($values);
foreach ($parameters as $key => $parameter) {
$this->db->pushErrorHandling(PEAR_ERROR_RETURN);
$this->db->expectError(MDB2_ERROR_NOT_FOUND);
$err = $this->bindValue($parameter, $values[$parameter], $types[$key]);
$this->db->popExpect();
$this->db->popErrorHandling();
if (PEAR::isError($err)) {
if ($err->getCode() == MDB2_ERROR_NOT_FOUND) {
//ignore (extra value for missing placeholder)
continue;
}
return $err;
}
}
return MDB2_OK;
}
// }}}
// {{{ function bindParam($parameter, &$value, $type = null)
/**
* Bind a variable to a parameter of a prepared query.
*
* @param int the order number of the parameter in the query
* statement. The order number of the first parameter is 1.
* @param mixed variable that is meant to be bound to specified
* parameter. The type of the value depends on the $type argument.
* @param string specifies the type of the field
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function bindParam($parameter, &$value, $type = null)
{
if (!is_numeric($parameter)) {
$parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
}
if (!in_array($parameter, $this->positions)) {
return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
}
$this->values[$parameter] =$value;
if (!is_null($type)) {
$this->types[$parameter] = $type;
}
return MDB2_OK;
}
// }}}
// {{{ function bindParamArray(&$values, $types = null)
/**
* Bind the variables of multiple a parameter of a prepared query in bulk.
*
* @param array specifies all necessary information
* for bindParam() the array elements must use keys corresponding to
* the number of the position of the parameter.
* @param array specifies the types of the fields
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
* @see bindParam()
*/
function bindParamArray(&$values, $types = null)
{
$types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
$parameters = array_keys($values);
foreach ($parameters as $key => $parameter) {
$err = $this->bindParam($parameter, $values[$parameter], $types[$key]);
if (PEAR::isError($err)) {
return $err;
}
}
return MDB2_OK;
}
// }}}
// {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false)
/**
* Execute a prepared query statement.
*
* @param array specifies all necessary information
* for bindParam() the array elements must use keys corresponding
* to the number of the position of the parameter.
* @param mixed specifies which result class to use
* @param mixed specifies which class to wrap results in
*
* @return mixed MDB2_Result or integer (affected rows) on success,
* a MDB2 error on failure
* @access public
*/
function &execute($values = null, $result_class = true, $result_wrap_class = false)
{
if (is_null($this->positions)) {
return $this->db->raiseError(MDB2_ERROR, null, null,
'Prepared statement has already been freed', __FUNCTION__);
}
$values = (array)$values;
if (!empty($values)) {
$err = $this->bindValueArray($values);
if (PEAR::isError($err)) {
return $this->db->raiseError(MDB2_ERROR, null, null,
'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__);
}
}
$result =$this->_execute($result_class, $result_wrap_class);
return $result;
}
// }}}
// {{{ function &_execute($result_class = true, $result_wrap_class = false)
/**
* Execute a prepared query statement helper method.
*
* @param mixed specifies which result class to use
* @param mixed specifies which class to wrap results in
*
* @return mixed MDB2_Result or integer (affected rows) on success,
* a MDB2 error on failure
* @access private
*/
function &_execute($result_class = true, $result_wrap_class = false)
{
$this->last_query = $this->query;
$query = '';
$last_position = 0;
foreach ($this->positions as $current_position => $parameter) {
if (!array_key_exists($parameter, $this->values)) {
return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
}
$value = $this->values[$parameter];
$query.= substr($this->query, $last_position, $current_position - $last_position);
if (!isset($value)) {
$value_quoted = 'NULL';
} else {
$type = !empty($this->types[$parameter]) ? $this->types[$parameter] : null;
$value_quoted = $this->db->quote($value, $type);
if (PEAR::isError($value_quoted)) {
return $value_quoted;
}
}
$query.= $value_quoted;
$last_position = $current_position + 1;
}
$query.= substr($this->query, $last_position);
$this->db->offset = $this->offset;
$this->db->limit = $this->limit;
if ($this->is_manip) {
$result = $this->db->exec($query);
} else {
$result =$this->db->query($query, $this->result_types, $result_class, $result_wrap_class);
}
return $result;
}
// }}}
// {{{ function free()
/**
* Release resources allocated for the specified prepared query.
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function free()
{
if (is_null($this->positions)) {
return $this->db->raiseError(MDB2_ERROR, null, null,
'Prepared statement has already been freed', __FUNCTION__);
}
$this->statement = null;
$this->positions = null;
$this->query = null;
$this->types = null;
$this->result_types = null;
$this->limit = null;
$this->is_manip = null;
$this->offset = null;
$this->values = null;
return MDB2_OK;
}
// }}}
}
// }}}
// {{{ class MDB2_Module_Common
/**
* The common modules class for MDB2 module objects
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Module_Common
{
// {{{ Variables (Properties)
/**
* contains the key to the global MDB2 instance array of the associated
* MDB2 instance
*
* @var int
* @access protected
*/
var $db_index;
// }}}
// {{{ constructor: function __construct($db_index)
/**
* Constructor
*/
function __construct($db_index)
{
$this->db_index = $db_index;
}
// }}}
// {{{ function &getDBInstance()
/**
* Get the instance of MDB2 associated with the module instance
*
* @return object MDB2 instance or a MDB2 error on failure
*
* @access public
*/
function getDBInstance()
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$result =$GLOBALS['_MDB2_databases'][$this->db_index];
} else {
$result =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'could not find MDB2 instance');
}
return $result;
}
// }}}
}
// }}}
// {{{ function MDB2_closeOpenTransactions()
/**
* Close any open transactions form persistent connections
*
* @return void
*
* @access public
*/
function MDB2_closeOpenTransactions()
{
reset($GLOBALS['_MDB2_databases']);
while (next($GLOBALS['_MDB2_databases'])) {
$key = key($GLOBALS['_MDB2_databases']);
if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent
&& $GLOBALS['_MDB2_databases'][$key]->in_transaction
) {
$GLOBALS['_MDB2_databases'][$key]->rollback();
}
}
}
// }}}
// {{{ function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null)
/**
* default debug output handler
*
* @param object reference to an MDB2 database object
* @param string usually the method name that triggered the debug call:
* for example 'query', 'prepare', 'execute', 'parameters',
* 'beginTransaction', 'commit', 'rollback'
* @param string message that should be appended to the debug variable
* @param array contains context information about the debug() call
* common keys are: is_manip, time, result etc.
*
* @return void|string optionally return a modified message, this allows
* rewriting a query before being issued or prepared
*
* @access public
*/
function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array())
{
$db->debug_output.= $scope.'('.$db->db_index.'): ';
$db->debug_output.= $message.$db->getOption('log_line_break');
return $message;
}
// }}}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: Date.php,v 1.10 2006/03/01 12:15:32 lsmith Exp $
//
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
/**
* Several methods to convert the MDB2 native timestamp format (ISO based)
* to and from data structures that are convenient to worth with in side of php.
* For more complex date arithmetic please take a look at the Date package in PEAR
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Date
{
// {{{ mdbNow()
/**
* return the current datetime
*
* @return string current datetime in the MDB2 format
* @access public
*/
function mdbNow()
{
return date('Y-m-d H:i:s');
}
// }}}
// {{{ mdbToday()
/**
* return the current date
*
* @return string current date in the MDB2 format
* @access public
*/
function mdbToday()
{
return date('Y-m-d');
}
// }}}
// {{{ mdbTime()
/**
* return the current time
*
* @return string current time in the MDB2 format
* @access public
*/
function mdbTime()
{
return date('H:i:s');
}
// }}}
// {{{ date2Mdbstamp()
/**
* convert a date into a MDB2 timestamp
*
* @param int hour of the date
* @param int minute of the date
* @param int second of the date
* @param int month of the date
* @param int day of the date
* @param int year of the date
*
* @return string a valid MDB2 timestamp
* @access public
*/
function date2Mdbstamp($hour = null, $minute = null, $second = null,
$month = null, $day = null, $year = null)
{
return MDB2_Date::unix2Mdbstamp(mktime($hour, $minute, $second, $month, $day, $year, -1));
}
// }}}
// {{{ unix2Mdbstamp()
/**
* convert a unix timestamp into a MDB2 timestamp
*
* @param int a valid unix timestamp
*
* @return string a valid MDB2 timestamp
* @access public
*/
function unix2Mdbstamp($unix_timestamp)
{
return date('Y-m-d H:i:s', $unix_timestamp);
}
// }}}
// {{{ mdbstamp2Unix()
/**
* convert a MDB2 timestamp into a unix timestamp
*
* @param int a valid MDB2 timestamp
* @return string unix timestamp with the time stored in the MDB2 format
*
* @access public
*/
function mdbstamp2Unix($mdb_timestamp)
{
$arr = MDB2_Date::mdbstamp2Date($mdb_timestamp);
return mktime($arr['hour'], $arr['minute'], $arr['second'], $arr['month'], $arr['day'], $arr['year'], -1);
}
// }}}
// {{{ mdbstamp2Date()
/**
* convert a MDB2 timestamp into an array containing all
* values necessary to pass to php's date() function
*
* @param int a valid MDB2 timestamp
*
* @return array with the time split
* @access public
*/
function mdbstamp2Date($mdb_timestamp)
{
list($arr['year'], $arr['month'], $arr['day'], $arr['hour'], $arr['minute'], $arr['second']) =
sscanf($mdb_timestamp, "%04u-%02u-%02u %02u:%02u:%02u");
return $arr;
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: Common.php,v 1.139 2008/12/04 11:50:42 afz Exp $
require_once('MDB2/LOB.php');
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
/**
* MDB2_Driver_Common: Base class that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Datatype');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_Common extends MDB2_Module_Common
{
var $valid_default_values = array(
'text' => '',
'boolean' => true,
'integer' => 0,
'decimal' => 0.0,
'float' => 0.0,
'timestamp' => '1970-01-01 00:00:00',
'time' => '00:00:00',
'date' => '1970-01-01',
'clob' => '',
'blob' => '',
);
/**
* contains all LOB objects created with this MDB2 instance
* @var array
* @access protected
*/
var $lobs = array();
// }}}
// {{{ getValidTypes()
/**
* Get the list of valid types
*
* This function returns an array of valid types as keys with the values
* being possible default values for all native datatypes and mapped types
* for custom datatypes.
*
* @return mixed array on success, a MDB2 error on failure
* @access public
*/
function getValidTypes()
{
$types = $this->valid_default_values;
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map'])) {
foreach ($db->options['datatype_map'] as $type => $mapped_type) {
if (array_key_exists($mapped_type, $types)) {
$types[$type] = $types[$mapped_type];
} elseif (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type, 'mapped_type' => $mapped_type);
$default = call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
$types[$type] = $default;
}
}
}
return $types;
}
// }}}
// {{{ checkResultTypes()
/**
* Define the list of types to be associated with the columns of a given
* result set.
*
* This function may be called before invoking fetchRow(), fetchOne()
* fetchCole() and fetchAll() so that the necessary data type
* conversions are performed on the data to be retrieved by them. If this
* function is not called, the type of all result set columns is assumed
* to be text, thus leading to not perform any conversions.
*
* @param array $types array variable that lists the
* data types to be expected in the result set columns. If this array
* contains less types than the number of columns that are returned
* in the result set, the remaining columns are assumed to be of the
* type text. Currently, the types clob and blob are not fully
* supported.
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function checkResultTypes($types)
{
$types = is_array($types) ? $types : array($types);
foreach ($types as $key => $type) {
if (!isset($this->valid_default_values[$type])) {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (empty($db->options['datatype_map'][$type])) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
$type.' for '.$key.' is not a supported column type', __FUNCTION__);
}
}
}
return $types;
}
// }}}
// {{{ _baseConvertResult()
/**
* General type conversion method
*
* @param mixed $value reference to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object an MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
switch ($type) {
case 'text':
if ($rtrim) {
$value = rtrim($value);
}
return $value;
case 'integer':
return intval($value);
case 'boolean':
return !empty($value);
case 'decimal':
return $value;
case 'float':
return doubleval($value);
case 'date':
return $value;
case 'time':
return $value;
case 'timestamp':
return $value;
case 'clob':
case 'blob':
$this->lobs[] = array(
'buffer' => null,
'position' => 0,
'lob_index' => null,
'endOfLOB' => false,
'resource' => $value,
'value' => null,
'loaded' => false,
);
end($this->lobs);
$lob_index = key($this->lobs);
$this->lobs[$lob_index]['lob_index'] = $lob_index;
return fopen('MDB2LOB://'.$lob_index.'@'.$this->db_index, 'r+');
}
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_INVALID, null, null,
'attempt to convert result value to an unknown type :' . $type, __FUNCTION__);
}
// }}}
// {{{ convertResult()
/**
* Convert a value to a RDBMS indipendent MDB2 type
*
* @param mixed $value value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return mixed converted value
* @access public
*/
function convertResult($value, $type, $rtrim = true)
{
if (is_null($value)) {
return null;
}
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map'][$type])) {
$type = $db->options['datatype_map'][$type];
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type, 'value' => $value, 'rtrim' => $rtrim);
return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
}
}
return $this->_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ convertResultRow()
/**
* Convert a result row
*
* @param array $types
* @param array $row specifies the types to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return mixed MDB2_OK on success, an MDB2 error on failure
* @access public
*/
function convertResultRow($types, $row, $rtrim = true)
{
$types = $this->_sortResultFieldTypes(array_keys($row), $types);
foreach ($row as $key => $value) {
if (empty($types[$key])) {
continue;
}
$value = $this->convertResult($row[$key], $types[$key], $rtrim);
if (PEAR::isError($value)) {
return $value;
}
$row[$key] = $value;
}
return $row;
}
// }}}
// {{{ _sortResultFieldTypes()
/**
* convert a result row
*
* @param array $types
* @param array $row specifies the types to convert to
* @param bool $rtrim if to rtrim text values or not
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function _sortResultFieldTypes($columns, $types)
{
$n_cols = count($columns);
$n_types = count($types);
if ($n_cols > $n_types) {
for ($i= $n_cols - $n_types; $i >= 0; $i--) {
$types[] = null;
}
}
$sorted_types = array();
foreach ($columns as $col) {
$sorted_types[$col] = null;
}
foreach ($types as $name => $type) {
if (array_key_exists($name, $sorted_types)) {
$sorted_types[$name] = $type;
unset($types[$name]);
}
}
// if there are left types in the array, fill the null values of the
// sorted array with them, in order.
if (count($types)) {
reset($types);
foreach (array_keys($sorted_types) as $k) {
if (is_null($sorted_types[$k])) {
$sorted_types[$k] = current($types);
next($types);
}
}
}
return $sorted_types;
}
// }}}
// {{{ getDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare
* of the given type
*
* @param string $type type to which the value should be converted to
* @param string $name name the field to be declared.
* @param string $field definition of the field
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getDeclaration($type, $name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map'][$type])) {
$type = $db->options['datatype_map'][$type];
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type, 'name' => $name, 'field' => $field);
return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
}
$field['type'] = $type;
}
if (!method_exists($this, "_get{$type}Declaration")) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'type not defined: '.$type, __FUNCTION__);
}
return $this->{"_get{$type}Declaration"}($name, $field);
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length']) ? $field['length'] : $db->options['default_text_field_length'];
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
return 'TEXT';
case 'blob':
return 'TEXT';
case 'integer':
return 'INT';
case 'boolean':
return 'INT';
case 'date':
return 'CHAR ('.strlen('YYYY-MM-DD').')';
case 'time':
return 'CHAR ('.strlen('HH:MM:SS').')';
case 'timestamp':
return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
case 'float':
return 'TEXT';
case 'decimal':
return 'TEXT';
}
return '';
}
// }}}
// {{{ _getDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare a generic type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* charset
* Text value with the default CHARACTER SET for this field.
* collation
* Text value with the default COLLATION for this field.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field, or a MDB2_Error on failure
* @access protected
*/
function _getDeclaration($name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$declaration_options = $db->datatype->_getDeclarationOptions($field);
if (PEAR::isError($declaration_options)) {
return $declaration_options;
}
return $name.' '.$this->getTypeDeclaration($field).$declaration_options;
}
// }}}
// {{{ _getDeclarationOptions()
/**
* Obtain DBMS specific SQL code portion needed to declare a generic type
* field to be used in statement like CREATE TABLE, without the field name
* and type values (ie. just the character set, default value, if the
* field is permitted to be NULL or not, and the collation options).
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Text value to be used as default for this field.
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* charset
* Text value with the default CHARACTER SET for this field.
* collation
* Text value with the default COLLATION for this field.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field's options.
* @access protected
*/
function _getDeclarationOptions($field)
{
$charset = empty($field['charset']) ? '' :
' '.$this->_getCharsetFieldDeclaration($field['charset']);
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$default = '';
if (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$valid_default_values = $this->getValidTypes();
$field['default'] = $valid_default_values[$field['type']];
if ($field['default'] === ''&& ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)) {
$field['default'] = ' ';
}
}
if (!is_null($field['default'])) {
$default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
}
}
$collation = empty($field['collation']) ? '' :
' '.$this->_getCollationFieldDeclaration($field['collation']);
return $charset.$default.$notnull.$collation;
}
// }}}
// {{{ _getCharsetFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $charset name of the charset
* @return string DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration.
*/
function _getCharsetFieldDeclaration($charset)
{
return '';
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field should be
* declared as unsigned integer if possible.
*
* default
* Integer value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
if (!empty($field['unsigned'])) {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
}
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ _getTextDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getTextDeclaration($name, $field)
{
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ _getCLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an character
* large object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function _getCLOBDeclaration($name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _getBLOBDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an binary large
* object type field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the large
* object field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getBLOBDeclaration($name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$notnull;
}
// }}}
// {{{ _getBooleanDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare a boolean type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Boolean value to be used as default for this field.
*
* notnullL
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getBooleanDeclaration($name, $field)
{
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ _getDateDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare a date type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Date value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getDateDeclaration($name, $field)
{
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ _getTimestampDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare a timestamp
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Timestamp value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getTimestampDeclaration($name, $field)
{
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ _getTimeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare a time
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Time value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getTimeDeclaration($name, $field)
{
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ _getFloatDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare a float type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Float value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getFloatDeclaration($name, $field)
{
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ _getDecimalDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare a decimal type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Decimal value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getDecimalDeclaration($name, $field)
{
return $this->_getDeclaration($name, $field);
}
// }}}
// {{{ compareDefinition()
/**
* Obtain an array of changes that may need to applied
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access public
*/
function compareDefinition($current, $previous)
{
$type = !empty($current['type']) ? $current['type'] : null;
if (!method_exists($this, "_compare{$type}Definition")) {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('current' => $current, 'previous' => $previous);
$change = call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
return $change;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'type "'.$current['type'].'" is not yet supported', __FUNCTION__);
}
if (empty($previous['type']) || $previous['type'] != $type) {
return $current;
}
$change = $this->{"_compare{$type}Definition"}($current, $previous);
if ($previous['type'] != $type) {
$change['type'] = true;
}
$previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false;
$notnull = !empty($current['notnull']) ? $current['notnull'] : false;
if ($previous_notnull != $notnull) {
$change['notnull'] = true;
}
$previous_default = array_key_exists('default', $previous) ? $previous['default'] :
($previous_notnull ? '' : null);
$default = array_key_exists('default', $current) ? $current['default'] :
($notnull ? '' : null);
if ($previous_default !== $default) {
$change['default'] = true;
}
return $change;
}
// }}}
// {{{ _compareIntegerDefinition()
/**
* Obtain an array of changes that may need to applied to an integer field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareIntegerDefinition($current, $previous)
{
$change = array();
$previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false;
$unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false;
if ($previous_unsigned != $unsigned) {
$change['unsigned'] = true;
}
$previous_autoincrement = !empty($previous['autoincrement']) ? $previous['autoincrement'] : false;
$autoincrement = !empty($current['autoincrement']) ? $current['autoincrement'] : false;
if ($previous_autoincrement != $autoincrement) {
$change['autoincrement'] = true;
}
return $change;
}
// }}}
// {{{ _compareTextDefinition()
/**
* Obtain an array of changes that may need to applied to an text field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareTextDefinition($current, $previous)
{
$change = array();
$previous_length = !empty($previous['length']) ? $previous['length'] : 0;
$length = !empty($current['length']) ? $current['length'] : 0;
if ($previous_length != $length) {
$change['length'] = true;
}
$previous_fixed = !empty($previous['fixed']) ? $previous['fixed'] : 0;
$fixed = !empty($current['fixed']) ? $current['fixed'] : 0;
if ($previous_fixed != $fixed) {
$change['fixed'] = true;
}
return $change;
}
// }}}
// {{{ _compareCLOBDefinition()
/**
* Obtain an array of changes that may need to applied to an CLOB field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareCLOBDefinition($current, $previous)
{
return $this->_compareTextDefinition($current, $previous);
}
// }}}
// {{{ _compareBLOBDefinition()
/**
* Obtain an array of changes that may need to applied to an BLOB field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareBLOBDefinition($current, $previous)
{
return $this->_compareTextDefinition($current, $previous);
}
// }}}
// {{{ _compareDateDefinition()
/**
* Obtain an array of changes that may need to applied to an date field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareDateDefinition($current, $previous)
{
return array();
}
// }}}
// {{{ _compareTimeDefinition()
/**
* Obtain an array of changes that may need to applied to an time field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareTimeDefinition($current, $previous)
{
return array();
}
// }}}
// {{{ _compareTimestampDefinition()
/**
* Obtain an array of changes that may need to applied to an timestamp field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareTimestampDefinition($current, $previous)
{
return array();
}
// }}}
// {{{ _compareBooleanDefinition()
/**
* Obtain an array of changes that may need to applied to an boolean field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareBooleanDefinition($current, $previous)
{
return array();
}
// }}}
// {{{ _compareFloatDefinition()
/**
* Obtain an array of changes that may need to applied to an float field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareFloatDefinition($current, $previous)
{
return array();
}
// }}}
// {{{ _compareDecimalDefinition()
/**
* Obtain an array of changes that may need to applied to an decimal field
*
* @param array $current new definition
* @param array $previous old definition
* @return array containing all changes that will need to be applied
* @access protected
*/
function _compareDecimalDefinition($current, $previous)
{
return array();
}
// }}}
// {{{ quote()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param string $type type to which the value should be converted to
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access public
*/
function quote($value, $type = null, $quote = true, $escape_wildcards = false)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (is_null($value)
|| ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
) {
if (!$quote) {
return null;
}
return 'NULL';
}
if (is_null($type)) {
switch (gettype($value)) {
case 'integer':
$type = 'integer';
break;
case 'double':
// todo: default to decimal as float is quite unusual
// $type = 'float';
$type = 'decimal';
break;
case 'boolean':
$type = 'boolean';
break;
case 'array':
$value = serialize($value);
case 'object':
$type = 'text';
break;
default:
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
$type = 'timestamp';
} elseif (preg_match('/^\d{2}:\d{2}$/', $value)) {
$type = 'time';
} elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
$type = 'date';
} else {
$type = 'text';
}
break;
}
} elseif (!empty($db->options['datatype_map'][$type])) {
$type = $db->options['datatype_map'][$type];
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type, 'value' => $value, 'quote' => $quote, 'escape_wildcards' => $escape_wildcards);
return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
}
}
if (!method_exists($this, "_quote{$type}")) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'type not defined: '.$type, __FUNCTION__);
}
$value = $this->{"_quote{$type}"}($value, $quote, $escape_wildcards);
if ($quote && $escape_wildcards && $db->string_quoting['escape_pattern']
&& $db->string_quoting['escape'] !== $db->string_quoting['escape_pattern']
) {
$value.= $this->patternEscapeString();
}
return $value;
}
// }}}
// {{{ _quoteInteger()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteInteger($value, $quote, $escape_wildcards)
{
return (int)$value;
}
// }}}
// {{{ _quoteText()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that already contains any DBMS specific
* escaped character sequences.
* @access protected
*/
function _quoteText($value, $quote, $escape_wildcards)
{
if (!$quote) {
return $value;
}
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$value = $db->escape($value, $escape_wildcards);
if (PEAR::isError($value)) {
return $value;
}
return "'".$value."'";
}
// }}}
// {{{ _readFile()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _readFile($value)
{
$close = false;
if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
$close = true;
if ($match[1] == 'file://') {
$value = $match[2];
}
$value = @fopen($value, 'r');
}
if (is_resource($value)) {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$fp = $value;
$value = '';
while (!@feof($fp)) {
$value.= @fread($fp, $db->options['lob_buffer_length']);
}
if ($close) {
@fclose($fp);
}
}
return $value;
}
// }}}
// {{{ _quoteLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteLOB($value, $quote, $escape_wildcards)
{
$value = $this->_readFile($value);
if (PEAR::isError($value)) {
return $value;
}
return $this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteCLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteCLOB($value, $quote, $escape_wildcards)
{
return $this->_quoteLOB($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteBLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBLOB($value, $quote, $escape_wildcards)
{
return $this->_quoteLOB($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteBoolean()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBoolean($value, $quote, $escape_wildcards)
{
return ($value ? 1 : 0);
}
// }}}
// {{{ _quoteDate()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteDate($value, $quote, $escape_wildcards)
{
if ($value === 'CURRENT_DATE') {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
return $db->function->now('date');
}
return 'CURRENT_DATE';
}
return $this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteTimestamp()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteTimestamp($value, $quote, $escape_wildcards)
{
if ($value === 'CURRENT_TIMESTAMP') {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
return $db->function->now('timestamp');
}
return 'CURRENT_TIMESTAMP';
}
return $this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteTime()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteTime($value, $quote, $escape_wildcards)
{
if ($value === 'CURRENT_TIME') {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
return $db->function->now('time');
}
return 'CURRENT_TIME';
}
return $this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteFloat()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteFloat($value, $quote, $escape_wildcards)
{
if (preg_match('/^(.*)e([-+])(\d+)$/i', $value, $matches)) {
$decimal = $this->_quoteDecimal($matches[1], $quote, $escape_wildcards);
$sign = $matches[2];
$exponent = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
$value = $decimal.'E'.$sign.$exponent;
} else {
$value = $this->_quoteDecimal($value, $quote, $escape_wildcards);
}
return $value;
}
// }}}
// {{{ _quoteDecimal()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteDecimal($value, $quote, $escape_wildcards)
{
$value = (string)$value;
$value = preg_replace('/[^\d\.,\-+eE]/', '', $value);
if (preg_match('/[^\.\d]/', $value)) {
if (strpos($value, ',')) {
// 1000,00
if (!strpos($value, '.')) {
// convert the last "," to a "."
$value = strrev(str_replace(',', '.', strrev($value)));
// 1.000,00
} elseif (strpos($value, '.') && strpos($value, '.') < strpos($value, ',')) {
$value = str_replace('.', '', $value);
// convert the last "," to a "."
$value = strrev(str_replace(',', '.', strrev($value)));
// 1,000.00
} else {
$value = str_replace(',', '', $value);
}
}
}
return $value;
}
// }}}
// {{{ writeLOBToFile()
/**
* retrieve LOB from the database
*
* @param resource $lob stream handle
* @param string $file name of the file into which the LOb should be fetched
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access protected
*/
function writeLOBToFile($lob, $file)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
if ($match[1] == 'file://') {
$file = $match[2];
}
}
$fp = @fopen($file, 'wb');
while (!@feof($lob)) {
$result = @fread($lob, $db->options['lob_buffer_length']);
$read = strlen($result);
if (@fwrite($fp, $result, $read) != $read) {
@fclose($fp);
return $db->raiseError(MDB2_ERROR, null, null,
'could not write to the output file', __FUNCTION__);
}
}
@fclose($fp);
return MDB2_OK;
}
// }}}
// {{{ _retrieveLOB()
/**
* retrieve LOB from the database
*
* @param array $lob array
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access protected
*/
function _retrieveLOB(&$lob)
{
if (is_null($lob['value'])) {
$lob['value'] = $lob['resource'];
}
$lob['loaded'] = true;
return MDB2_OK;
}
// }}}
// {{{ readLOB()
/**
* Read data from large object input stream.
*
* @param resource $lob stream handle
* @param string $data reference to a variable that will hold data
* to be read from the large object input stream
* @param integer $length value that indicates the largest ammount ofdata
* to be read from the large object input stream.
* @return mixed the effective number of bytes read from the large object
* input stream on sucess or an MDB2 error object.
* @access public
* @see endOfLOB()
*/
function _readLOB($lob, $length)
{
return substr($lob['value'], $lob['position'], $length);
}
// }}}
// {{{ _endOfLOB()
/**
* Determine whether it was reached the end of the large object and
* therefore there is no more data to be read for the its input stream.
*
* @param array $lob array
* @return mixed true or false on success, a MDB2 error on failure
* @access protected
*/
function _endOfLOB($lob)
{
return $lob['endOfLOB'];
}
// }}}
// {{{ destroyLOB()
/**
* Free any resources allocated during the lifetime of the large object
* handler object.
*
* @param resource $lob stream handle
* @access public
*/
function destroyLOB($lob)
{
$lob_data = stream_get_meta_data($lob);
$lob_index = $lob_data['wrapper_data']->lob_index;
fclose($lob);
if (isset($this->lobs[$lob_index])) {
$this->_destroyLOB($this->lobs[$lob_index]);
unset($this->lobs[$lob_index]);
}
return MDB2_OK;
}
// }}}
// {{{ _destroyLOB()
/**
* Free any resources allocated during the lifetime of the large object
* handler object.
*
* @param array $lob array
* @access private
*/
function _destroyLOB(&$lob)
{
return MDB2_OK;
}
// }}}
// {{{ implodeArray()
/**
* apply a type to all values of an array and return as a comma seperated string
* useful for generating IN statements
*
* @access public
*
* @param array $array data array
* @param string $type determines type of the field
*
* @return string comma seperated values
*/
function implodeArray($array, $type = false)
{
if (!is_array($array) || empty($array)) {
return 'NULL';
}
if ($type) {
foreach ($array as $value) {
$return[] = $this->quote($value, $type);
}
} else {
$return = $array;
}
return implode(', ', $return);
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$match = '';
if (!is_null($operator)) {
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
if (is_null($field)) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'case insensitive LIKE matching requires passing the field name', __FUNCTION__);
}
$db->loadModule('Function', null, true);
$match = $db->function->lower($field).' LIKE ';
break;
// case sensitive
case 'LIKE':
$match = is_null($field) ? 'LIKE ' : $field.' LIKE ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
if ($operator === 'ILIKE') {
$value = strtolower($value);
}
$escaped = $db->escape($value);
if (PEAR::isError($escaped)) {
return $escaped;
}
$match.= $db->escapePattern($escaped);
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ patternEscapeString()
/**
* build string to define pattern escape character
*
* @access public
*
* @return string define pattern escape character
*/
function patternEscapeString()
{
return '';
}
// }}}
// {{{ mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function mapNativeDatatype($field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
// If the user has specified an option to map the native field
// type to a custom MDB2 datatype...
$db_type = strtok($field['type'], '(), ');
if (!empty($db->options['nativetype_map_callback'][$db_type])) {
return call_user_func_array($db->options['nativetype_map_callback'][$db_type], array($db, $field));
}
// Otherwise perform the built-in (i.e. normal) MDB2 native type to
// MDB2 datatype conversion
return $this->_mapNativeDatatype($field);
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ mapPrepareDatatype()
/**
* Maps an mdb2 datatype to mysqli prepare type
*
* @param string $type
* @return string
* @access public
*/
function mapPrepareDatatype($type)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map'][$type])) {
$type = $db->options['datatype_map'][$type];
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type);
return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
}
}
return $type;
}
}
?>
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: mysql.php,v 1.65 2008/02/22 19:23:49 quipo Exp $
//
require_once('MDB2/Driver/Datatype/Common.php');
/**
* MDB2 MySQL driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_mysql extends MDB2_Driver_Datatype_Common
{
// {{{ _getCharsetFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $charset name of the charset
* @return string DBMS specific SQL code portion needed to set the CHARACTER SET
* of a field declaration.
*/
function _getCharsetFieldDeclaration($charset)
{
return 'CHARACTER SET '.$charset;
}
// }}}
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
if (empty($field['length']) && array_key_exists('default', $field)) {
$field['length'] = $db->varchar_max_length;
}
$length = !empty($field['length']) ? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR(255)')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYTEXT';
} elseif ($length <= 65532) {
return 'TEXT';
} elseif ($length <= 16777215) {
return 'MEDIUMTEXT';
}
}
return 'LONGTEXT';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYBLOB';
} elseif ($length <= 65532) {
return 'BLOB';
} elseif ($length <= 16777215) {
return 'MEDIUMBLOB';
}
}
return 'LONGBLOB';
case 'integer':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 1) {
return 'TINYINT';
} elseif ($length == 2) {
return 'SMALLINT';
} elseif ($length == 3) {
return 'MEDIUMINT';
} elseif ($length == 4) {
return 'INT';
} elseif ($length > 4) {
return 'BIGINT';
}
}
return 'INT';
case 'boolean':
return 'TINYINT(1)';
case 'date':
return 'DATE';
case 'time':
return 'TIME';
case 'timestamp':
return 'DATETIME';
case 'float':
return 'DOUBLE';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' AUTO_INCREMENT PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
}
// }}}
// {{{ _getFloatDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an float type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned float if
* possible.
*
* default
* float value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getFloatDeclaration($name, $field)
{
// Since AUTO_INCREMENT can be used for integer or floating-point types,
// reuse the INTEGER declaration
// @see http://bugs.mysql.com/bug.php?id=31032
return $this->_getIntegerDeclaration($name, $field);
}
// }}}
// {{{ _getDecimalDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an decimal type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Decimal value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getDecimalDeclaration($name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$default = '';
if (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
} elseif (empty($field['notnull'])) {
$default = ' DEFAULT NULL';
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull;
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$match = '';
if (!is_null($operator)) {
$field = is_null($field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'LIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE BINARY ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$db_type = strtok($db_type, '(), ');
if ($db_type == 'national') {
$db_type = strtok('(), ');
}
if (!empty($field['length'])) {
$length = strtok($field['length'], ', ');
$decimal = strtok(', ');
} else {
$length = strtok('(), ');
$decimal = strtok('(), ');
}
$type = array();
$unsigned = $fixed = null;
switch ($db_type) {
case 'tinyint':
$type[] = 'integer';
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 1;
break;
case 'smallint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 2;
break;
case 'mediumint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 3;
break;
case 'int':
case 'integer':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 4;
break;
case 'bigint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 8;
break;
case 'tinytext':
case 'mediumtext':
case 'longtext':
case 'text':
case 'varchar':
$fixed = false;
case 'string':
case 'char':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
if ($decimal == 'binary') {
$type[] = 'blob';
}
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'enum':
$type[] = 'text';
preg_match_all('/\'.+\'/U', $field['type'], $matches);
$length = 0;
$fixed = false;
if (is_array($matches)) {
foreach ($matches[0] as $value) {
$length = max($length, strlen($value)-2);
}
if ($length == '1' && count($matches[0]) == 2) {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
}
$type[] = 'integer';
case 'set':
$fixed = false;
$type[] = 'text';
$type[] = 'integer';
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'datetime':
case 'timestamp':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'double':
case 'real':
$type[] = 'float';
$unsigned = preg_match('/ unsigned/i', $field['type']);
break;
case 'unknown':
case 'decimal':
case 'numeric':
$type[] = 'decimal';
$unsigned = preg_match('/ unsigned/i', $field['type']);
if ($decimal !== false) {
$length = $length.','.$decimal;
}
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
$type[] = 'blob';
$length = null;
break;
case 'binary':
case 'varbinary':
$type[] = 'blob';
break;
case 'year':
$type[] = 'integer';
$type[] = 'date';
$length = null;
break;
default:
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id: pgsql.php,v 1.93 2008/08/28 20:32:57 afz Exp $
require_once('MDB2/Driver/Datatype/Common.php');
/**
* MDB2 PostGreSQL driver
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Driver_Datatype_pgsql extends MDB2_Driver_Datatype_Common
{
// {{{ _baseConvertResult()
/**
* General type conversion method
*
* @param mixed $value refernce to a value to be converted
* @param string $type specifies which type to convert to
* @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
* @return object a MDB2 error on failure
* @access protected
*/
function _baseConvertResult($value, $type, $rtrim = true)
{
if (is_null($value)) {
return null;
}
switch ($type) {
case 'boolean':
return $value == 't';
case 'float':
return doubleval($value);
case 'date':
return $value;
case 'time':
return substr($value, 0, strlen('HH:MM:SS'));
case 'timestamp':
return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS'));
case 'blob':
$value = pg_unescape_bytea($value);
return parent::_baseConvertResult($value, $type, $rtrim);
}
return parent::_baseConvertResult($value, $type, $rtrim);
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length']) ? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
return 'TEXT';
case 'blob':
return 'BYTEA';
case 'integer':
if (!empty($field['autoincrement'])) {
if (!empty($field['length'])) {
$length = $field['length'];
if ($length > 4) {
return 'BIGSERIAL PRIMARY KEY';
}
}
return 'SERIAL PRIMARY KEY';
}
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 2) {
return 'SMALLINT';
} elseif ($length == 3 || $length == 4) {
return 'INT';
} elseif ($length > 4) {
return 'BIGINT';
}
}
return 'INT';
case 'boolean':
return 'BOOLEAN';
case 'date':
return 'DATE';
case 'time':
return 'TIME without time zone';
case 'timestamp':
return 'TIMESTAMP without time zone';
case 'float':
return 'FLOAT8';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'NUMERIC('.$length.','.$scale.')';
}
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field should be
* declared as unsigned integer if possible.
*
* default
* Integer value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($field['unsigned'])) {
$db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
}
if (!empty($field['autoincrement'])) {
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field);
}
$default = '';
if (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$default.$notnull;
}
// }}}
// {{{ _quoteCLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteCLOB($value, $quote, $escape_wildcards)
{
return $this->_quoteText($value, $quote, $escape_wildcards);
}
// }}}
// {{{ _quoteBLOB()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBLOB($value, $quote, $escape_wildcards)
{
if (!$quote) {
return $value;
}
if (version_compare(PHP_VERSION, '5.2.0RC6', '>=')) {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$connection = $db->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$value = @pg_escape_bytea($connection, $value);
} else {
$value = @pg_escape_bytea($value);
}
return "'".$value."'";
}
// }}}
// {{{ _quoteBoolean()
/**
* Convert a text value into a DBMS specific format that is suitable to
* compose query statements.
*
* @param string $value text string value that is intended to be converted.
* @param bool $quote determines if the value should be quoted and escaped
* @param bool $escape_wildcards if to escape escape wildcards
* @return string text string that represents the given argument value in
* a DBMS specific format.
* @access protected
*/
function _quoteBoolean($value, $quote, $escape_wildcards)
{
$value = $value ? 't' : 'f';
if (!$quote) {
return $value;
}
return "'".$value."'";
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$match = '';
if (!is_null($operator)) {
$field = is_null($field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'ILIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ patternEscapeString()
/**
* build string to define escape pattern string
*
* @access public
*
*
* @return string define escape pattern
*/
function patternEscapeString()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return ' ESCAPE '.$this->quote($db->string_quoting['escape_pattern']);
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$length = $field['length'];
$type = array();
$unsigned = $fixed = null;
switch ($db_type) {
case 'smallint':
case 'int2':
$type[] = 'integer';
$unsigned = false;
$length = 2;
if ($length == '2') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
}
break;
case 'int':
case 'int4':
case 'integer':
case 'serial':
case 'serial4':
$type[] = 'integer';
$unsigned = false;
$length = 4;
break;
case 'bigint':
case 'int8':
case 'bigserial':
case 'serial8':
$type[] = 'integer';
$unsigned = false;
$length = 8;
break;
case 'bool':
case 'boolean':
$type[] = 'boolean';
$length = null;
break;
case 'text':
case 'varchar':
$fixed = false;
case 'unknown':
case 'char':
case 'bpchar':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'datetime':
case 'timestamp':
case 'timestamptz':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'float4':
case 'float8':
case 'double':
case 'real':
$type[] = 'float';
break;
case 'decimal':
case 'money':
case 'numeric':
$type[] = 'decimal';
if (isset($field['scale'])) {
$length = $length.','.$field['scale'];
}
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
case 'bytea':
$type[] = 'blob';
$length = null;
break;
case 'oid':
$type[] = 'blob';
$type[] = 'clob';
$length = null;
break;
case 'year':
$type[] = 'integer';
$type[] = 'date';
$length = null;
break;
default:
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
// {{{ mapPrepareDatatype()
/**
* Maps an mdb2 datatype to native prepare type
*
* @param string $type
* @return string
* @access public
*/
function mapPrepareDatatype($type)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($db->options['datatype_map'][$type])) {
$type = $db->options['datatype_map'][$type];
if (!empty($db->options['datatype_map_callback'][$type])) {
$parameter = array('type' => $type);
return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
}
}
switch ($type) {
case 'integer':
return 'int';
case 'boolean':
return 'bool';
case 'decimal':
case 'float':
return 'numeric';
case 'clob':
return 'text';
case 'blob':
return 'bytea';
default:
break;
}
return $type;
}
// }}}
}
?>
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: sqlite.php,v 1.67 2008/02/22 19:58:06 quipo Exp $
//
require_once('MDB2/Driver/Datatype/Common.php');
/**
* MDB2 SQLite driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Datatype_sqlite extends MDB2_Driver_Datatype_Common
{
// {{{ _getCollationFieldDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration to be used in statements like CREATE TABLE.
*
* @param string $collation name of the collation
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a field declaration.
*/
function _getCollationFieldDeclaration($collation)
{
return 'COLLATE '.$collation;
}
// }}}
// {{{ getTypeDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
*
* @param array $field associative array with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* field. If this argument is missing the field should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access public
*/
function getTypeDeclaration($field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
switch ($field['type']) {
case 'text':
$length = !empty($field['length'])
? $field['length'] : false;
$fixed = !empty($field['fixed']) ? $field['fixed'] : false;
return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
: ($length ? 'VARCHAR('.$length.')' : 'TEXT');
case 'clob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYTEXT';
} elseif ($length <= 65532) {
return 'TEXT';
} elseif ($length <= 16777215) {
return 'MEDIUMTEXT';
}
}
return 'LONGTEXT';
case 'blob':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 255) {
return 'TINYBLOB';
} elseif ($length <= 65532) {
return 'BLOB';
} elseif ($length <= 16777215) {
return 'MEDIUMBLOB';
}
}
return 'LONGBLOB';
case 'integer':
if (!empty($field['length'])) {
$length = $field['length'];
if ($length <= 2) {
return 'SMALLINT';
} elseif ($length == 3 || $length == 4) {
return 'INTEGER';
} elseif ($length > 4) {
return 'BIGINT';
}
}
return 'INTEGER';
case 'boolean':
return 'BOOLEAN';
case 'date':
return 'DATE';
case 'time':
return 'TIME';
case 'timestamp':
return 'DATETIME';
case 'float':
return 'DOUBLE'.($db->options['fixed_float'] ? '('.
($db->options['fixed_float']+2).','.$db->options['fixed_float'].')' : '');
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
$scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
return 'DECIMAL('.$length.','.$scale.')';
}
return '';
}
// }}}
// {{{ _getIntegerDeclaration()
/**
* Obtain DBMS specific SQL code portion needed to declare an integer type
* field to be used in statements like CREATE TABLE.
*
* @param string $name name the field to be declared.
* @param string $field associative array with the name of the properties
* of the field being declared as array indexes.
* Currently, the types of supported field
* properties are as follows:
*
* unsigned
* Boolean flag that indicates whether the field
* should be declared as unsigned integer if
* possible.
*
* default
* Integer value to be used as default for this
* field.
*
* notnull
* Boolean flag that indicates whether this field is
* constrained to not be set to null.
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
* @access protected
*/
function _getIntegerDeclaration($name, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$default = $autoinc = '';
if (!empty($field['autoincrement'])) {
$autoinc = ' PRIMARY KEY';
} elseif (array_key_exists('default', $field)) {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
}
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
$name = $db->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
}
// }}}
// {{{ matchPattern()
/**
* build a pattern matching string
*
* @access public
*
* @param array $pattern even keys are strings, odd are patterns (% and _)
* @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
* @param string $field optional field name that is being matched against
* (might be required when emulating ILIKE)
*
* @return string SQL pattern
*/
function matchPattern($pattern, $operator = null, $field = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$match = '';
if (!is_null($operator)) {
$field = is_null($field) ? '' : $field.' ';
$operator = strtoupper($operator);
switch ($operator) {
// case insensitive
case 'ILIKE':
$match = $field.'LIKE ';
break;
// case sensitive
case 'LIKE':
$match = $field.'LIKE ';
break;
default:
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'not a supported operator type:'. $operator, __FUNCTION__);
}
}
$match.= "'";
foreach ($pattern as $key => $value) {
if ($key % 2) {
$match.= $value;
} else {
$match.= $db->escapePattern($db->escape($value));
}
}
$match.= "'";
$match.= $this->patternEscapeString();
return $match;
}
// }}}
// {{{ _mapNativeDatatype()
/**
* Maps a native array description of a field to a MDB2 datatype and length
*
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
* @access public
*/
function _mapNativeDatatype($field)
{
$db_type = strtolower($field['type']);
$length = !empty($field['length']) ? $field['length'] : null;
$unsigned = !empty($field['unsigned']) ? $field['unsigned'] : null;
$fixed = null;
$type = array();
switch ($db_type) {
case 'boolean':
$type[] = 'boolean';
break;
case 'tinyint':
$type[] = 'integer';
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 1;
break;
case 'smallint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 2;
break;
case 'mediumint':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 3;
break;
case 'int':
case 'integer':
case 'serial':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 4;
break;
case 'bigint':
case 'bigserial':
$type[] = 'integer';
$unsigned = preg_match('/ unsigned/i', $field['type']);
$length = 8;
break;
case 'clob':
$type[] = 'clob';
$fixed = false;
break;
case 'tinytext':
case 'mediumtext':
case 'longtext':
case 'text':
case 'varchar':
case 'varchar2':
$fixed = false;
case 'char':
$type[] = 'text';
if ($length == '1') {
$type[] = 'boolean';
if (preg_match('/^(is|has)/', $field['name'])) {
$type = array_reverse($type);
}
} elseif (strstr($db_type, 'text')) {
$type[] = 'clob';
$type = array_reverse($type);
}
if ($fixed !== false) {
$fixed = true;
}
break;
case 'date':
$type[] = 'date';
$length = null;
break;
case 'datetime':
case 'timestamp':
$type[] = 'timestamp';
$length = null;
break;
case 'time':
$type[] = 'time';
$length = null;
break;
case 'float':
case 'double':
case 'real':
$type[] = 'float';
break;
case 'decimal':
case 'numeric':
$type[] = 'decimal';
$length = $length.','.$field['decimal'];
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
$type[] = 'blob';
$length = null;
break;
case 'year':
$type[] = 'integer';
$type[] = 'date';
$length = null;
break;
default:
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unknown database attribute type: '.$db_type, __FUNCTION__);
}
if ((int)$length <= 0) {
$length = null;
}
return array($type, $length, $unsigned, $fixed);
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: Common.php,v 1.21 2008/02/17 18:51:39 quipo Exp $
//
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
/**
* Base class for the function modules that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Function');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_Common extends MDB2_Module_Common
{
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
*
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
// {{{ functionTable()
/**
* return string for internal table used when calling only a function
*
* @return string for internal table used when calling only a function
* @access public
*/
function functionTable()
{
return '';
}
// }}}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time:
* - CURRENT_TIMESTAMP (date and time, TIMESTAMP type)
* - CURRENT_DATE (date, DATE type)
* - CURRENT_TIME (time, TIME type)
*
* @param string $type 'timestamp' | 'time' | 'date'
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'time':
return 'CURRENT_TIME';
case 'date':
return 'CURRENT_DATE';
case 'timestamp':
default:
return 'CURRENT_TIMESTAMP';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (!is_null($length)) {
return "SUBSTRING($value FROM $position FOR $length)";
}
return "SUBSTRING($value FROM $position)";
}
// }}}
// {{{ replace()
/**
* return string to call a function to get replace inside an SQL statement.
*
* @return string to call a function to get a replace
* @access public
*/
function replace($str, $from_str, $to_str)
{
return "REPLACE($str, $from_str , $to_str)";
}
// }}}
// {{{ concat()
/**
* Returns string to concatenate two or more string parameters
*
* @param string $value1
* @param string $value2
* @param string $values...
*
* @return string to concatenate two strings
* @access public
*/
function concat($value1, $value2)
{
$args = func_get_args();
return "(".implode(' || ', $args).")";
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return 'RAND()';
}
// }}}
// {{{ lower()
/**
* return string to call a function to lower the case of an expression
*
* @param string $expression
*
* @return return string to lower case of an expression
* @access public
*/
function lower($expression)
{
return "LOWER($expression)";
}
// }}}
// {{{ upper()
/**
* return string to call a function to upper the case of an expression
*
* @param string $expression
*
* @return return string to upper case of an expression
* @access public
*/
function upper($expression)
{
return "UPPER($expression)";
}
// }}}
// {{{ length()
/**
* return string to call a function to get the length of a string expression
*
* @param string $expression
*
* @return return string to get the string expression length
* @access public
*/
function length($expression)
{
return "LENGTH($expression)";
}
// }}}
// {{{ guid()
/**
* Returns global unique identifier
*
* @return string to get global unique identifier
* @access public
*/
function guid()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: mysql.php,v 1.12 2008/02/17 18:54:08 quipo Exp $
//
require_once('MDB2/Driver/Function/Common.php');
/**
* MDB2 MySQL driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_mysql extends MDB2_Driver_Function_Common
{
// }}}
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'CALL '.$name;
$query .= $params ? '('.implode(', ', $params).')' : '()';
return $db->query($query, $types, $result_class, $result_wrap_class);
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'UNIX_TIMESTAMP('. $expression.')';
}
// }}}
// {{{ concat()
/**
* Returns string to concatenate two or more string parameters
*
* @param string $value1
* @param string $value2
* @param string $values...
* @return string to concatenate two strings
* @access public
**/
function concat($value1, $value2)
{
$args = func_get_args();
return "CONCAT(".implode(', ', $args).")";
}
// }}}
// {{{ guid()
/**
* Returns global unique identifier
*
* @return string to get global unique identifier
* @access public
*/
function guid()
{
return 'UUID()';
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id: pgsql.php,v 1.11 2008/11/09 19:46:50 quipo Exp $
require_once('MDB2/Driver/Function/Common.php');
/**
* MDB2 MySQL driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_pgsql extends MDB2_Driver_Function_Common
{
// {{{ executeStoredProc()
/**
* Execute a stored procedure and return any results
*
* @param string $name string that identifies the function to execute
* @param mixed $params array that contains the paramaters to pass the stored proc
* @param mixed $types array that contains the types of the columns in
* the result set
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
* @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SELECT * FROM '.$name;
$query .= $params ? '('.implode(', ', $params).')' : '()';
return $db->query($query, $types, $result_class, $result_wrap_class);
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'EXTRACT(EPOCH FROM DATE_TRUNC(\'seconds\', CAST ((' . $expression . ') AS TIMESTAMP)))';
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return 'RANDOM()';
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: sqlite.php,v 1.10 2008/02/17 18:54:08 quipo Exp $
//
require_once('MDB2/Driver/Function/Common.php');
/**
* MDB2 SQLite driver for the function modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Function_sqlite extends MDB2_Driver_Function_Common
{
// {{{ constructor
/**
* Constructor
*/
function __construct($db_index)
{
parent::__construct($db_index);
// create all sorts of UDFs
}
// {{{ now()
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time.
*
* @return string to call a variable with the current timestamp
* @access public
*/
function now($type = 'timestamp')
{
switch ($type) {
case 'time':
return 'time(\'now\')';
case 'date':
return 'date(\'now\')';
case 'timestamp':
default:
return 'datetime(\'now\')';
}
}
// }}}
// {{{ unixtimestamp()
/**
* return string to call a function to get the unix timestamp from a iso timestamp
*
* @param string $expression
*
* @return string to call a variable with the timestamp
* @access public
*/
function unixtimestamp($expression)
{
return 'strftime("%s",'. $expression.', "utc")';
}
// }}}
// {{{ substring()
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @access public
*/
function substring($value, $position = 1, $length = null)
{
if (!is_null($length)) {
return "substr($value,$position,$length)";
}
return "substr($value,$position,length($value))";
}
// }}}
// {{{ random()
/**
* return string to call a function to get random value inside an SQL statement
*
* @return return string to generate float between 0 and 1
* @access public
*/
function random()
{
return '((RANDOM()+2147483648)/4294967296)';
}
// }}}
// {{{ replace()
/**
* return string to call a function to get a replacement inside an SQL statement.
*
* @return string to call a function to get a replace
* @access public
*/
function replace($str, $from_str, $to_str)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
return $error;
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id: Common.php,v 1.72 2009/01/14 15:00:40 quipo Exp $
//
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
/**
* Base class for the management modules that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Manager');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Manager_Common extends MDB2_Module_Common
{
// {{{ splitTableSchema()
/**
* Split the "[owner|schema].table" notation into an array
*
* @param string $table [schema and] table name
*
* @return array array(schema, table)
* @access private
*/
function splitTableSchema($table)
{
$ret = array();
if (strpos($table, '.') !== false) {
return explode('.', $table);
}
return array(null, $table);
}
// }}}
// {{{ getFieldDeclarationList()
/**
* Get declaration of a number of field in bulk
*
* @param array $fields a multidimensional associative array.
* The first dimension determines the field name, while the second
* dimension is keyed with the name of the properties
* of the field being declared as array indexes. Currently, the types
* of supported field properties are as follows:
*
* default
* Boolean value to be used as default for this field.
*
* notnull
* Boolean flag that indicates whether this field is constrained
* to not be set to null.
*
* @return mixed string on success, a MDB2 error on failure
* @access public
*/
function getFieldDeclarationList($fields)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!is_array($fields) || empty($fields)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'missing any fields', __FUNCTION__);
}
foreach ($fields as $field_name => $field) {
$query = $db->getDeclaration($field['type'], $field_name, $field);
if (PEAR::isError($query)) {
return $query;
}
$query_fields[] = $query;
}
return implode(', ', $query_fields);
}
// }}}
// {{{ _fixSequenceName()
/**
* Removes any formatting in an sequence name using the 'seqname_format' option
*
* @param string $sqn string that containts name of a potential sequence
* @param bool $check if only formatted sequences should be returned
* @return string name of the sequence with possible formatting removed
* @access protected
*/
function _fixSequenceName($sqn, $check = false)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$seq_pattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $db->options['seqname_format']).'$/i';
$seq_name = preg_replace($seq_pattern, '\\1', $sqn);
if ($seq_name && !strcasecmp($sqn, $db->getSequenceName($seq_name))) {
return $seq_name;
}
if ($check) {
return false;
}
return $sqn;
}
// }}}
// {{{ _fixIndexName()
/**
* Removes any formatting in an index name using the 'idxname_format' option
*
* @param string $idx string that containts name of anl index
* @return string name of the index with eventual formatting removed
* @access protected
*/
function _fixIndexName($idx)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$idx_pattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $db->options['idxname_format']).'$/i';
$idx_name = preg_replace($idx_pattern, '\\1', $idx);
if ($idx_name && !strcasecmp($idx, $db->getIndexName($idx_name))) {
return $idx_name;
}
return $idx;
}
// }}}
// {{{ createDatabase()
/**
* create a new database
*
* @param string $name name of the database that should be created
* @param array $options array with charset, collation info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createDatabase($database, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ alterDatabase()
/**
* alter an existing database
*
* @param string $name name of the database that should be created
* @param array $options array with charset, collation info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function alterDatabase($database, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ dropDatabase()
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropDatabase($database)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ _getCreateTableQuery()
/**
* Create a basic SQL query for a new table creation
*
* @param string $name Name of the database that should be created
* @param array $fields Associative array that contains the definition of each field of the new table
* @param array $options An associative array of table options
*
* @return mixed string (the SQL query) on success, a MDB2 error on failure
* @see createTable()
*/
function _getCreateTableQuery($name, $fields, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!$name) {
return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
'no valid table name specified', __FUNCTION__);
}
if (empty($fields)) {
return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
'no fields specified for table "'.$name.'"', __FUNCTION__);
}
$query_fields = $this->getFieldDeclarationList($fields);
if (PEAR::isError($query_fields)) {
return $query_fields;
}
if (!empty($options['primary'])) {
$query_fields.= ', PRIMARY KEY ('.implode(', ', array_keys($options['primary'])).')';
}
$name = $db->quoteIdentifier($name, true);
$result = 'CREATE ';
if (!empty($options['temporary'])) {
$result .= $this->_getTemporaryTableQuery();
}
$result .= " TABLE $name ($query_fields)";
return $result;
}
// }}}
// {{{ _getTemporaryTableQuery()
/**
* A method to return the required SQL string that fits between CREATE ... TABLE
* to create the table as a temporary table.
*
* Should be overridden in driver classes to return the correct string for the
* specific database type.
*
* The default is to return the string "TEMPORARY" - this will result in a
* SQL error for any database that does not support temporary tables, or that
* requires a different SQL command from "CREATE TEMPORARY TABLE".
*
* @return string The string required to be placed between "CREATE" and "TABLE"
* to generate a temporary table, if possible.
*/
function _getTemporaryTableQuery()
{
return 'TEMPORARY';
}
// }}}
// {{{ createTable()
/**
* create a new table
*
* @param string $name Name of the database that should be created
* @param array $fields Associative array that contains the definition of each field of the new table
* The indexes of the array entries are the names of the fields of the table an
* the array entry values are associative arrays like those that are meant to be
* passed with the field definitions to get[Type]Declaration() functions.
* array(
* 'id' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* 'notnull' => 1
* 'default' => 0
* ),
* 'name' => array(
* 'type' => 'text',
* 'length' => 12
* ),
* 'password' => array(
* 'type' => 'text',
* 'length' => 12
* )
* );
* @param array $options An associative array of table options:
* array(
* 'comment' => 'Foo',
* 'temporary' => true|false,
* );
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createTable($name, $fields, $options = array())
{
$query = $this->_getCreateTableQuery($name, $fields, $options);
if (PEAR::isError($query)) {
return $query;
}
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$result = $db->exec($query);
if (PEAR::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ dropTable()
/**
* drop an existing table
*
* @param string $name name of the table that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropTable($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
return $db->exec("DROP TABLE $name");
}
// }}}
// {{{ truncateTable()
/**
* Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
* it falls back to a DELETE FROM TABLE query)
*
* @param string $name name of the table that should be truncated
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function truncateTable($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
return $db->exec("DELETE FROM $name");
}
// }}}
// {{{ vacuum()
/**
* Optimize (vacuum) all the tables in the db (or only the specified table)
* and optionally run ANALYZE.
*
* @param string $table table name (all the tables if empty)
* @param array $options an array with driver-specific options:
* - timeout [int] (in seconds) [mssql-only]
* - analyze [boolean] [pgsql and mysql]
* - full [boolean] [pgsql-only]
* - freeze [boolean] [pgsql-only]
*
* @return mixed MDB2_OK success, a MDB2 error on failure
* @access public
*/
function vacuum($table = null, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ alterTable()
/**
* alter an existing table
*
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @access public
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function alterTable($name, $changes, $check)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listDatabases()
/**
* list all databases
*
* @return mixed array of database names on success, a MDB2 error on failure
* @access public
*/
function listDatabases()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implementedd', __FUNCTION__);
}
// }}}
// {{{ listUsers()
/**
* list all users
*
* @return mixed array of user names on success, a MDB2 error on failure
* @access public
*/
function listUsers()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listViews()
/**
* list all views in the current database
*
* @param string database, the current is default
* NB: not all the drivers can get the view names from
* a database other than the current one
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listViews($database = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listTableViews()
/**
* list the views in the database that reference a given table
*
* @param string table for which all referenced views should be found
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listTableViews($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listTableTriggers()
/**
* list all triggers in the database that reference a given table
*
* @param string table for which all referenced triggers should be found
* @return mixed array of trigger names on success, a MDB2 error on failure
* @access public
*/
function listTableTriggers($table = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listFunctions()
/**
* list all functions in the current database
*
* @return mixed array of function names on success, a MDB2 error on failure
* @access public
*/
function listFunctions()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listTables()
/**
* list all tables in the current database
*
* @param string database, the current is default.
* NB: not all the drivers can get the table names from
* a database other than the current one
* @return mixed array of table names on success, a MDB2 error on failure
* @access public
*/
function listTables($database = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listTableFields()
/**
* list all fields in a table in the current database
*
* @param string $table name of table that should be used in method
* @return mixed array of field names on success, a MDB2 error on failure
* @access public
*/
function listTableFields($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ createIndex()
/**
* Get the stucture of a field into an array
*
* @param string $table name of the table on which the index is to be created
* @param string $name name of the index to be created
* @param array $definition associative array that defines properties of the index to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the index fields as array
* indexes. Each entry of this array is set to another type of associative
* array that specifies properties of the index that are specific to
* each field.
*
* Currently, only the sorting property is supported. It should be used
* to define the sorting direction of the index. It may be set to either
* ascending or descending.
*
* Not all DBMS support index sorting direction configuration. The DBMS
* drivers of those that do not support it ignore this property. Use the
* function supports() to determine whether the DBMS driver can manage indexes.
*
* Example
* array(
* 'fields' => array(
* 'user_name' => array(
* 'sorting' => 'ascending'
* ),
* 'last_login' => array()
* )
* )
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createIndex($table, $name, $definition)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
$query = "CREATE INDEX $name ON $table";
$fields = array();
foreach (array_keys($definition['fields']) as $field) {
$fields[] = $db->quoteIdentifier($field, true);
}
$query .= ' ('. implode(', ', $fields) . ')';
return $db->exec($query);
}
// }}}
// {{{ dropIndex()
/**
* drop existing index
*
* @param string $table name of table that should be used in method
* @param string $name name of the index to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropIndex($table, $name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($db->getIndexName($name), true);
return $db->exec("DROP INDEX $name");
}
// }}}
// {{{ listTableIndexes()
/**
* list all indexes in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of index names on success, a MDB2 error on failure
* @access public
*/
function listTableIndexes($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ _getAdvancedFKOptions()
/**
* Return the FOREIGN KEY query section dealing with non-standard options
* as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
*
* @param array $definition
* @return string
* @access protected
*/
function _getAdvancedFKOptions($definition)
{
return '';
}
// }}}
// {{{ createConstraint()
/**
* create a constraint on a table
*
* @param string $table name of the table on which the constraint is to be created
* @param string $name name of the constraint to be created
* @param array $definition associative array that defines properties of the constraint to be created.
* The full structure of the array looks like this:
* <pre>
* array (
* [primary] => 0
* [unique] => 0
* [foreign] => 1
* [check] => 0
* [fields] => array (
* [field1name] => array() // one entry per each field covered
* [field2name] => array() // by the index
* [field3name] => array(
* [sorting] => ascending
* [position] => 3
* )
* )
* [references] => array(
* [table] => name
* [fields] => array(
* [field1name] => array( //one entry per each referenced field
* [position] => 1
* )
* )
* )
* [deferrable] => 0
* [initiallydeferred] => 0
* [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
* [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
* [match] => SIMPLE|PARTIAL|FULL
* );
* </pre>
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createConstraint($table, $name, $definition)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
$query = "ALTER TABLE $table ADD CONSTRAINT $name";
if (!empty($definition['primary'])) {
$query.= ' PRIMARY KEY';
} elseif (!empty($definition['unique'])) {
$query.= ' UNIQUE';
} elseif (!empty($definition['foreign'])) {
$query.= ' FOREIGN KEY';
}
$fields = array();
foreach (array_keys($definition['fields']) as $field) {
$fields[] = $db->quoteIdentifier($field, true);
}
$query .= ' ('. implode(', ', $fields) . ')';
if (!empty($definition['foreign'])) {
$query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true);
$referenced_fields = array();
foreach (array_keys($definition['references']['fields']) as $field) {
$referenced_fields[] = $db->quoteIdentifier($field, true);
}
$query .= ' ('. implode(', ', $referenced_fields) . ')';
$query .= $this->_getAdvancedFKOptions($definition);
}
return $db->exec($query);
}
// }}}
// {{{ dropConstraint()
/**
* drop existing constraint
*
* @param string $table name of table that should be used in method
* @param string $name name of the constraint to be dropped
* @param string $primary hint if the constraint is primary
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropConstraint($table, $name, $primary = false)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
return $db->exec("ALTER TABLE $table DROP CONSTRAINT $name");
}
// }}}
// {{{ listTableConstraints()
/**
* list all constraints in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of constraint names on success, a MDB2 error on failure
* @access public
*/
function listTableConstraints($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ createSequence()
/**
* create sequence
*
* @param string $seq_name name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createSequence($seq_name, $start = 1)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ dropSequence()
/**
* drop existing sequence
*
* @param string $seq_name name of the sequence to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropSequence($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ listSequences()
/**
* list all sequences in the current database
*
* @param string database, the current is default
* NB: not all the drivers can get the sequence names from
* a database other than the current one
* @return mixed array of sequence names on success, a MDB2 error on failure
* @access public
*/
function listSequences($database = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: mysql.php,v 1.113 2008/11/23 20:30:29 quipo Exp $
//
require_once('MDB2/Driver/Manager/Common.php');
/**
* MDB2 MySQL driver for the management modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Manager_mysql extends MDB2_Driver_Manager_Common
{
// }}}
// {{{ createDatabase()
/**
* create a new database
*
* @param string $name name of the database that should be created
* @param array $options array with charset, collation info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createDatabase($name, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = 'CREATE DATABASE ' . $name;
if (!empty($options['charset'])) {
$query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text');
}
if (!empty($options['collation'])) {
$query .= ' COLLATE ' . $db->quote($options['collation'], 'text');
}
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ alterDatabase()
/**
* alter an existing database
*
* @param string $name name of the database that is intended to be changed
* @param array $options array with charset, collation info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function alterDatabase($name, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true);
if (!empty($options['charset'])) {
$query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text');
}
if (!empty($options['collation'])) {
$query .= ' COLLATE ' . $db->quote($options['collation'], 'text');
}
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ dropDatabase()
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropDatabase($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = "DROP DATABASE $name";
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ _getAdvancedFKOptions()
/**
* Return the FOREIGN KEY query section dealing with non-standard options
* as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
*
* @param array $definition
* @return string
* @access protected
*/
function _getAdvancedFKOptions($definition)
{
$query = '';
if (!empty($definition['match'])) {
$query .= ' MATCH '.$definition['match'];
}
if (!empty($definition['onupdate'])) {
$query .= ' ON UPDATE '.$definition['onupdate'];
}
if (!empty($definition['ondelete'])) {
$query .= ' ON DELETE '.$definition['ondelete'];
}
return $query;
}
// }}}
// {{{ createTable()
/**
* create a new table
*
* @param string $name Name of the database that should be created
* @param array $fields Associative array that contains the definition of each field of the new table
* The indexes of the array entries are the names of the fields of the table an
* the array entry values are associative arrays like those that are meant to be
* passed with the field definitions to get[Type]Declaration() functions.
* array(
* 'id' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* 'notnull' => 1
* 'default' => 0
* ),
* 'name' => array(
* 'type' => 'text',
* 'length' => 12
* ),
* 'password' => array(
* 'type' => 'text',
* 'length' => 12
* )
* );
* @param array $options An associative array of table options:
* array(
* 'comment' => 'Foo',
* 'charset' => 'utf8',
* 'collate' => 'utf8_unicode_ci',
* 'type' => 'innodb',
* );
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createTable($name, $fields, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
// if we have an AUTO_INCREMENT column and a PK on more than one field,
// we have to handle it differently...
$autoincrement = null;
if (empty($options['primary'])) {
$pk_fields = array();
foreach ($fields as $fieldname => $def) {
if (!empty($def['primary'])) {
$pk_fields[$fieldname] = true;
}
if (!empty($def['autoincrement'])) {
$autoincrement = $fieldname;
}
}
if (!is_null($autoincrement) && count($pk_fields) > 1) {
$options['primary'] = $pk_fields;
} else {
// the PK constraint is on max one field => OK
$autoincrement = null;
}
}
$query = $this->_getCreateTableQuery($name, $fields, $options);
if (PEAR::isError($query)) {
return $query;
}
if (!is_null($autoincrement)) {
// we have to remove the PK clause added by _getIntegerDeclaration()
$query = str_replace('AUTO_INCREMENT PRIMARY KEY', 'AUTO_INCREMENT', $query);
}
$options_strings = array();
if (!empty($options['comment'])) {
$options_strings['comment'] = 'COMMENT = '.$db->quote($options['comment'], 'text');
}
if (!empty($options['charset'])) {
$options_strings['charset'] = 'DEFAULT CHARACTER SET '.$options['charset'];
if (!empty($options['collate'])) {
$options_strings['charset'].= ' COLLATE '.$options['collate'];
}
}
$type = false;
if (!empty($options['type'])) {
$type = $options['type'];
} elseif ($db->options['default_table_type']) {
$type = $db->options['default_table_type'];
}
if ($type) {
$options_strings[] = "ENGINE = $type";
}
if (!empty($options_strings)) {
$query .= ' '.implode(' ', $options_strings);
}
$result = $db->exec($query);
if (PEAR::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ dropTable()
/**
* drop an existing table
*
* @param string $name name of the table that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropTable($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
//delete the triggers associated to existing FK constraints
$constraints = $this->listTableConstraints($name);
if (!PEAR::isError($constraints) && !empty($constraints)) {
$db->loadModule('Reverse', null, true);
foreach ($constraints as $constraint) {
$definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
$result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']);
if (PEAR::isError($result)) {
return $result;
}
}
}
}
return parent::dropTable($name);
}
// }}}
// {{{ truncateTable()
/**
* Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
* it falls back to a DELETE FROM TABLE query)
*
* @param string $name name of the table that should be truncated
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function truncateTable($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
return $db->exec("TRUNCATE TABLE $name");
}
// }}}
// {{{ vacuum()
/**
* Optimize (vacuum) all the tables in the db (or only the specified table)
* and optionally run ANALYZE.
*
* @param string $table table name (all the tables if empty)
* @param array $options an array with driver-specific options:
* - timeout [int] (in seconds) [mssql-only]
* - analyze [boolean] [pgsql and mysql]
* - full [boolean] [pgsql-only]
* - freeze [boolean] [pgsql-only]
*
* @return mixed MDB2_OK success, a MDB2 error on failure
* @access public
*/
function vacuum($table = null, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (empty($table)) {
$table = $this->listTables();
if (PEAR::isError($table)) {
return $table;
}
}
if (is_array($table)) {
foreach (array_keys($table) as $k) {
$table[$k] = $db->quoteIdentifier($table[$k], true);
}
$table = implode(', ', $table);
} else {
$table = $db->quoteIdentifier($table, true);
}
$result = $db->exec('OPTIMIZE TABLE '.$table);
if (PEAR::isError($result)) {
return $result;
}
if (!empty($options['analyze'])) {
return $db->exec('ANALYZE TABLE '.$table);
}
return MDB2_OK;
}
// }}}
// {{{ alterTable()
/**
* alter an existing table
*
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @access public
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function alterTable($name, $changes, $check)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
foreach ($changes as $change_name => $change) {
switch ($change_name) {
case 'add':
case 'remove':
case 'change':
case 'rename':
case 'name':
break;
default:
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'change type "'.$change_name.'" not yet supported', __FUNCTION__);
}
}
if ($check) {
return MDB2_OK;
}
$query = '';
if (!empty($changes['name'])) {
$change_name = $db->quoteIdentifier($changes['name'], true);
$query .= 'RENAME TO ' . $change_name;
}
if (!empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $field_name => $field) {
if ($query) {
$query.= ', ';
}
$query.= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
}
}
if (!empty($changes['remove']) && is_array($changes['remove'])) {
foreach ($changes['remove'] as $field_name => $field) {
if ($query) {
$query.= ', ';
}
$field_name = $db->quoteIdentifier($field_name, true);
$query.= 'DROP ' . $field_name;
}
}
$rename = array();
if (!empty($changes['rename']) && is_array($changes['rename'])) {
foreach ($changes['rename'] as $field_name => $field) {
$rename[$field['name']] = $field_name;
}
}
if (!empty($changes['change']) && is_array($changes['change'])) {
foreach ($changes['change'] as $field_name => $field) {
if ($query) {
$query.= ', ';
}
if (isset($rename[$field_name])) {
$old_field_name = $rename[$field_name];
unset($rename[$field_name]);
} else {
$old_field_name = $field_name;
}
$old_field_name = $db->quoteIdentifier($old_field_name, true);
$query.= "CHANGE $old_field_name " . $db->getDeclaration($field['definition']['type'], $field_name, $field['definition']);
}
}
if (!empty($rename) && is_array($rename)) {
foreach ($rename as $rename_name => $renamed_field) {
if ($query) {
$query.= ', ';
}
$field = $changes['rename'][$renamed_field];
$renamed_field = $db->quoteIdentifier($renamed_field, true);
$query.= 'CHANGE ' . $renamed_field . ' ' . $db->getDeclaration($field['definition']['type'], $field['name'], $field['definition']);
}
}
if (!$query) {
return MDB2_OK;
}
$name = $db->quoteIdentifier($name, true);
return $db->exec("ALTER TABLE $name $query");
}
// }}}
// {{{ listDatabases()
/**
* list all databases
*
* @return mixed array of database names on success, a MDB2 error on failure
* @access public
*/
function listDatabases()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$result = $db->queryCol('SHOW DATABASES');
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listUsers()
/**
* list all users
*
* @return mixed array of user names on success, a MDB2 error on failure
* @access public
*/
function listUsers()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->queryCol('SELECT DISTINCT USER FROM mysql.USER');
}
// }}}
// {{{ listFunctions()
/**
* list all functions in the current database
*
* @return mixed array of function names on success, a MDB2 error on failure
* @access public
*/
function listFunctions()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT name FROM mysql.proc";
/*
SELECT ROUTINE_NAME
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'FUNCTION'
*/
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableTriggers()
/**
* list all triggers in the database that reference a given table
*
* @param string table for which all referenced triggers should be found
* @return mixed array of trigger names on success, a MDB2 error on failure
* @access public
*/
function listTableTriggers($table = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SHOW TRIGGERS';
if (!is_null($table)) {
$table = $db->quote($table, 'text');
$query .= " LIKE $table";
}
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTables()
/**
* list all tables in the current database
*
* @param string database, the current is default
* @return mixed array of table names on success, a MDB2 error on failure
* @access public
*/
function listTables($database = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SHOW /*!50002 FULL*/ TABLES";
if (!is_null($database)) {
$query .= " FROM $database";
}
$query.= "/*!50002 WHERE Table_type = 'BASE TABLE'*/";
$table_names = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED);
if (PEAR::isError($table_names)) {
return $table_names;
}
$result = array();
foreach ($table_names as $table) {
if (!$this->_fixSequenceName($table[0], true)) {
$result[] = $table[0];
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listViews()
/**
* list all views in the current database
*
* @param string database, the current is default
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listViews($database = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SHOW FULL TABLES';
if (!is_null($database)) {
$query.= " FROM $database";
}
$query.= " WHERE Table_type = 'VIEW'";
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableFields()
/**
* list all fields in a table in the current database
*
* @param string $table name of table that should be used in method
* @return mixed array of field names on success, a MDB2 error on failure
* @access public
*/
function listTableFields($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$result = $db->queryCol("SHOW COLUMNS FROM $table");
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ createIndex()
/**
* Get the stucture of a field into an array
*
* @author Leoncx
* @param string $table name of the table on which the index is to be created
* @param string $name name of the index to be created
* @param array $definition associative array that defines properties of the index to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the index fields as array
* indexes. Each entry of this array is set to another type of associative
* array that specifies properties of the index that are specific to
* each field.
*
* Currently, only the sorting property is supported. It should be used
* to define the sorting direction of the index. It may be set to either
* ascending or descending.
*
* Not all DBMS support index sorting direction configuration. The DBMS
* drivers of those that do not support it ignore this property. Use the
* function supports() to determine whether the DBMS driver can manage indexes.
*
* Example
* array(
* 'fields' => array(
* 'user_name' => array(
* 'sorting' => 'ascending'
* 'length' => 10
* ),
* 'last_login' => array()
* )
* )
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createIndex($table, $name, $definition)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
$query = "CREATE INDEX $name ON $table";
$fields = array();
foreach ($definition['fields'] as $field => $fieldinfo) {
if (!empty($fieldinfo['length'])) {
$fields[] = $db->quoteIdentifier($field, true) . '(' . $fieldinfo['length'] . ')';
} else {
$fields[] = $db->quoteIdentifier($field, true);
}
}
$query .= ' ('. implode(', ', $fields) . ')';
return $db->exec($query);
}
// }}}
// {{{ dropIndex()
/**
* drop existing index
*
* @param string $table name of table that should be used in method
* @param string $name name of the index to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropIndex($table, $name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
return $db->exec("DROP INDEX $name ON $table");
}
// }}}
// {{{ listTableIndexes()
/**
* list all indexes in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of index names on success, a MDB2 error on failure
* @access public
*/
function listTableIndexes($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$key_name = 'Key_name';
$non_unique = 'Non_unique';
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$key_name = strtolower($key_name);
$non_unique = strtolower($non_unique);
} else {
$key_name = strtoupper($key_name);
$non_unique = strtoupper($non_unique);
}
}
$table = $db->quoteIdentifier($table, true);
$query = "SHOW INDEX FROM $table";
$indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($indexes)) {
return $indexes;
}
$result = array();
foreach ($indexes as $index_data) {
if ($index_data[$non_unique] && ($index = $this->_fixIndexName($index_data[$key_name]))) {
$result[$index] = true;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ createConstraint()
/**
* create a constraint on a table
*
* @param string $table name of the table on which the constraint is to be created
* @param string $name name of the constraint to be created
* @param array $definition associative array that defines properties of the constraint to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the constraint fields as array
* constraints. Each entry of this array is set to another type of associative
* array that specifies properties of the constraint that are specific to
* each field.
*
* Example
* array(
* 'fields' => array(
* 'user_name' => array(),
* 'last_login' => array()
* )
* )
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createConstraint($table, $name, $definition)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$type = '';
$idx_name = $db->quoteIdentifier($db->getIndexName($name), true);
if (!empty($definition['primary'])) {
$type = 'PRIMARY';
$idx_name = 'KEY';
} elseif (!empty($definition['unique'])) {
$type = 'UNIQUE';
} elseif (!empty($definition['foreign'])) {
$type = 'CONSTRAINT';
}
if (empty($type)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'invalid definition, could not create constraint', __FUNCTION__);
}
$table_quoted = $db->quoteIdentifier($table, true);
$query = "ALTER TABLE $table_quoted ADD $type $idx_name";
if (!empty($definition['foreign'])) {
$query .= ' FOREIGN KEY';
}
$fields = array();
foreach ($definition['fields'] as $field => $fieldinfo) {
$quoted = $db->quoteIdentifier($field, true);
if (!empty($fieldinfo['length'])) {
$quoted .= '(' . $fieldinfo['length'] . ')';
}
$fields[] = $quoted;
}
$query .= ' ('. implode(', ', $fields) . ')';
if (!empty($definition['foreign'])) {
$query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true);
$referenced_fields = array();
foreach (array_keys($definition['references']['fields']) as $field) {
$referenced_fields[] = $db->quoteIdentifier($field, true);
}
$query .= ' ('. implode(', ', $referenced_fields) . ')';
$query .= $this->_getAdvancedFKOptions($definition);
// add index on FK column(s) or we can't add a FK constraint
// @see http://forums.mysql.com/read.php?22,19755,226009
$result = $this->createIndex($table, $name.'_fkidx', $definition);
if (PEAR::isError($result)) {
return $result;
}
}
$res = $db->exec($query);
if (PEAR::isError($res)) {
return $res;
}
if (!empty($definition['foreign'])) {
return $this->_createFKTriggers($table, array($name => $definition));
}
return MDB2_OK;
}
// }}}
// {{{ dropConstraint()
/**
* drop existing constraint
*
* @param string $table name of table that should be used in method
* @param string $name name of the constraint to be dropped
* @param string $primary hint if the constraint is primary
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropConstraint($table, $name, $primary = false)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if ($primary || strtolower($name) == 'primary') {
$query = 'ALTER TABLE '. $db->quoteIdentifier($table, true) .' DROP PRIMARY KEY';
return $db->exec($query);
}
//is it a FK constraint? If so, also delete the associated triggers
$db->loadModule('Reverse', null, true);
$definition = $db->reverse->getTableConstraintDefinition($table, $name);
if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
//first drop the FK enforcing triggers
$result = $this->_dropFKTriggers($table, $name, $definition['references']['table']);
if (PEAR::isError($result)) {
return $result;
}
//then drop the constraint itself
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
$query = "ALTER TABLE $table DROP FOREIGN KEY $name";
return $db->exec($query);
}
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($db->getIndexName($name), true);
$query = "ALTER TABLE $table DROP INDEX $name";
return $db->exec($query);
}
// }}}
// {{{ _createFKTriggers()
/**
* Create triggers to enforce the FOREIGN KEY constraint on the table
*
* NB: since there's no RAISE_APPLICATION_ERROR facility in mysql,
* we call a non-existent procedure to raise the FK violation message.
* @see http://forums.mysql.com/read.php?99,55108,71877#msg-71877
*
* @param string $table table name
* @param array $foreign_keys FOREIGN KEY definitions
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access private
*/
function _createFKTriggers($table, $foreign_keys)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
// create triggers to enforce FOREIGN KEY constraints
if ($db->supports('triggers') && !empty($foreign_keys)) {
$table_quoted = $db->quoteIdentifier($table, true);
foreach ($foreign_keys as $fkname => $fkdef) {
if (empty($fkdef)) {
continue;
}
//set actions to default if not set
$fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']);
$fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']);
$trigger_names = array(
'insert' => $fkname.'_insert_trg',
'update' => $fkname.'_update_trg',
'pk_update' => $fkname.'_pk_update_trg',
'pk_delete' => $fkname.'_pk_delete_trg',
);
$table_fields = array_keys($fkdef['fields']);
$referenced_fields = array_keys($fkdef['references']['fields']);
//create the ON [UPDATE|DELETE] triggers on the primary table
$restrict_action = ' IF (SELECT ';
$aliased_fields = array();
foreach ($table_fields as $field) {
$aliased_fields[] = $table_quoted .'.'.$field .' AS '.$field;
}
$restrict_action .= implode(',', $aliased_fields)
.' FROM '.$table_quoted
.' WHERE ';
$conditions = array();
$new_values = array();
$null_values = array();
for ($i=0; $i<count($table_fields); $i++) {
$conditions[] = $table_fields[$i] .' = OLD.'.$referenced_fields[$i];
$new_values[] = $table_fields[$i] .' = NEW.'.$referenced_fields[$i];
$null_values[] = $table_fields[$i] .' = NULL';
}
$conditions2 = array();
for ($i=0; $i<count($referenced_fields); $i++) {
$conditions2[] = 'NEW.'.$referenced_fields[$i] .' <> OLD.'.$referenced_fields[$i];
}
$restrict_action .= implode(' AND ', $conditions).') IS NOT NULL'
.' AND (' .implode(' OR ', $conditions2) .')'
.' THEN CALL %s_ON_TABLE_'.$table.'_VIOLATES_FOREIGN_KEY_CONSTRAINT();'
.' END IF;';
$cascade_action_update = 'UPDATE '.$table_quoted.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions). ';';
$cascade_action_delete = 'DELETE FROM '.$table_quoted.' WHERE '.implode(' AND ', $conditions). ';';
$setnull_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions). ';';
if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) {
$db->loadModule('Reverse', null, true);
$default_values = array();
foreach ($table_fields as $table_field) {
$field_definition = $db->reverse->getTableFieldDefinition($table, $field);
if (PEAR::isError($field_definition)) {
return $field_definition;
}
$default_values[] = $table_field .' = '. $field_definition[0]['default'];
}
$setdefault_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions). ';';
}
$query = 'CREATE TRIGGER %s'
.' %s ON '.$fkdef['references']['table']
.' FOR EACH ROW BEGIN '
.' SET FOREIGN_KEY_CHECKS = 0; '; //only really needed for ON UPDATE CASCADE
if ('CASCADE' == $fkdef['onupdate']) {
$sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $cascade_action_update;
} elseif ('SET NULL' == $fkdef['onupdate']) {
$sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action;
} elseif ('SET DEFAULT' == $fkdef['onupdate']) {
$sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action;
} elseif ('NO ACTION' == $fkdef['onupdate']) {
$sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update');
} elseif ('RESTRICT' == $fkdef['onupdate']) {
$sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update');
}
if ('CASCADE' == $fkdef['ondelete']) {
$sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $cascade_action_delete;
} elseif ('SET NULL' == $fkdef['ondelete']) {
$sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action;
} elseif ('SET DEFAULT' == $fkdef['ondelete']) {
$sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action;
} elseif ('NO ACTION' == $fkdef['ondelete']) {
$sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete');
} elseif ('RESTRICT' == $fkdef['ondelete']) {
$sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete');
}
$sql_update .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
$sql_delete .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
$db->pushErrorHandling(PEAR_ERROR_RETURN);
$db->expectError(MDB2_ERROR_CANNOT_CREATE);
$result = $db->exec($sql_delete);
$expected_errmsg = 'This MySQL version doesn\'t support multiple triggers with the same action time and event for one table';
$db->popExpect();
$db->popErrorHandling();
if (PEAR::isError($result)) {
if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
return $result;
}
$db->warnings[] = $expected_errmsg;
}
$db->pushErrorHandling(PEAR_ERROR_RETURN);
$db->expectError(MDB2_ERROR_CANNOT_CREATE);
$result = $db->exec($sql_update);
$db->popExpect();
$db->popErrorHandling();
if (PEAR::isError($result) && $result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
return $result;
}
$db->warnings[] = $expected_errmsg;
}
}
}
return MDB2_OK;
}
// }}}
// {{{ _dropFKTriggers()
/**
* Drop the triggers created to enforce the FOREIGN KEY constraint on the table
*
* @param string $table table name
* @param string $fkname FOREIGN KEY constraint name
* @param string $referenced_table referenced table name
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access private
*/
function _dropFKTriggers($table, $fkname, $referenced_table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$triggers = $this->listTableTriggers($table);
$triggers2 = $this->listTableTriggers($referenced_table);
if (!PEAR::isError($triggers2) && !PEAR::isError($triggers)) {
$triggers = array_merge($triggers, $triggers2);
$pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i';
foreach ($triggers as $trigger) {
if (preg_match($pattern, $trigger)) {
$result = $db->exec('DROP TRIGGER '.$trigger);
if (PEAR::isError($result)) {
return $result;
}
}
}
}
return MDB2_OK;
}
// }}}
// {{{ listTableConstraints()
/**
* list all constraints in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of constraint names on success, a MDB2 error on failure
* @access public
*/
function listTableConstraints($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$key_name = 'Key_name';
$non_unique = 'Non_unique';
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$key_name = strtolower($key_name);
$non_unique = strtolower($non_unique);
} else {
$key_name = strtoupper($key_name);
$non_unique = strtoupper($non_unique);
}
}
$query = 'SHOW INDEX FROM ' . $db->quoteIdentifier($table, true);
$indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($indexes)) {
return $indexes;
}
$result = array();
foreach ($indexes as $index_data) {
if (!$index_data[$non_unique]) {
if ($index_data[$key_name] !== 'PRIMARY') {
$index = $this->_fixIndexName($index_data[$key_name]);
} else {
$index = 'PRIMARY';
}
if (!empty($index)) {
$result[$index] = true;
}
}
}
//list FOREIGN KEY constraints...
$query = 'SHOW CREATE TABLE '. $db->escape($table);
$definition = $db->queryOne($query, 'text', 1);
if (!PEAR::isError($definition) && !empty($definition)) {
$pattern = '/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN KEY\b/Uims';
if (preg_match_all($pattern, str_replace('`', '', $definition), $matches) > 0) {
foreach ($matches[1] as $constraint) {
$result[$constraint] = true;
}
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ createSequence()
/**
* create sequence
*
* @param string $seq_name name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @param array $options An associative array of table options:
* array(
* 'comment' => 'Foo',
* 'charset' => 'utf8',
* 'collate' => 'utf8_unicode_ci',
* 'type' => 'innodb',
* );
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createSequence($seq_name, $start = 1, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
$seqcol_name = $db->quoteIdentifier($db->options['seqcol_name'], true);
$options_strings = array();
if (!empty($options['comment'])) {
$options_strings['comment'] = 'COMMENT = '.$db->quote($options['comment'], 'text');
}
if (!empty($options['charset'])) {
$options_strings['charset'] = 'DEFAULT CHARACTER SET '.$options['charset'];
if (!empty($options['collate'])) {
$options_strings['charset'].= ' COLLATE '.$options['collate'];
}
}
$type = false;
if (!empty($options['type'])) {
$type = $options['type'];
} elseif ($db->options['default_table_type']) {
$type = $db->options['default_table_type'];
}
if ($type) {
$options_strings[] = "ENGINE = $type";
}
$query = "CREATE TABLE $sequence_name ($seqcol_name INT NOT NULL AUTO_INCREMENT, PRIMARY KEY ($seqcol_name))";
if (!empty($options_strings)) {
$query .= ' '.implode(' ', $options_strings);
}
$res = $db->exec($query);
if (PEAR::isError($res)) {
return $res;
}
if ($start == 1) {
return MDB2_OK;
}
$query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')';
$res = $db->exec($query);
if (!PEAR::isError($res)) {
return MDB2_OK;
}
// Handle error
$result = $db->exec("DROP TABLE $sequence_name");
if (PEAR::isError($result)) {
return $db->raiseError($result, null, null,
'could not drop inconsistent sequence table', __FUNCTION__);
}
return $db->raiseError($res, null, null,
'could not create sequence table', __FUNCTION__);
}
// }}}
// {{{ dropSequence()
/**
* drop existing sequence
*
* @param string $seq_name name of the sequence to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropSequence($seq_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
return $db->exec("DROP TABLE $sequence_name");
}
// }}}
// {{{ listSequences()
/**
* list all sequences in the current database
*
* @param string database, the current is default
* @return mixed array of sequence names on success, a MDB2 error on failure
* @access public
*/
function listSequences($database = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SHOW TABLES";
if (!is_null($database)) {
$query .= " FROM $database";
}
$table_names = $db->queryCol($query);
if (PEAR::isError($table_names)) {
return $table_names;
}
$result = array();
foreach ($table_names as $table_name) {
if ($sqn = $this->_fixSequenceName($table_name, true)) {
$result[] = $sqn;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id: pgsql.php,v 1.87 2008/11/29 14:09:59 afz Exp $
require_once('MDB2/Driver/Manager/Common.php');
/**
* MDB2 MySQL driver for the management modules
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Driver_Manager_pgsql extends MDB2_Driver_Manager_Common
{
// {{{ createDatabase()
/**
* create a new database
*
* @param string $name name of the database that should be created
* @param array $options array with charset info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createDatabase($name, $options = array())
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = 'CREATE DATABASE ' . $name;
if (!empty($options['charset'])) {
$query .= ' WITH ENCODING ' . $db->quote($options['charset'], 'text');
}
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ alterDatabase()
/**
* alter an existing database
*
* @param string $name name of the database that is intended to be changed
* @param array $options array with name, owner info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function alterDatabase($name, $options = array())
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true);
if (!empty($options['name'])) {
$query .= ' RENAME TO ' . $options['name'];
}
if (!empty($options['owner'])) {
$query .= ' OWNER TO ' . $options['owner'];
}
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ dropDatabase()
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropDatabase($name)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
$query = "DROP DATABASE $name";
return $db->standaloneQuery($query, null, true);
}
// }}}
// {{{ _getAdvancedFKOptions()
/**
* Return the FOREIGN KEY query section dealing with non-standard options
* as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
*
* @param array $definition
* @return string
* @access protected
*/
function _getAdvancedFKOptions($definition)
{
$query = '';
if (!empty($definition['match'])) {
$query .= ' MATCH '.$definition['match'];
}
if (!empty($definition['onupdate'])) {
$query .= ' ON UPDATE '.$definition['onupdate'];
}
if (!empty($definition['ondelete'])) {
$query .= ' ON DELETE '.$definition['ondelete'];
}
if (!empty($definition['deferrable'])) {
$query .= ' DEFERRABLE';
} else {
$query .= ' NOT DEFERRABLE';
}
if (!empty($definition['initiallydeferred'])) {
$query .= ' INITIALLY DEFERRED';
} else {
$query .= ' INITIALLY IMMEDIATE';
}
return $query;
}
// }}}
// {{{ truncateTable()
/**
* Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
* it falls back to a DELETE FROM TABLE query)
*
* @param string $name name of the table that should be truncated
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function truncateTable($name)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->quoteIdentifier($name, true);
return $db->exec("TRUNCATE TABLE $name");
}
// }}}
// {{{ vacuum()
/**
* Optimize (vacuum) all the tables in the db (or only the specified table)
* and optionally run ANALYZE.
*
* @param string $table table name (all the tables if empty)
* @param array $options an array with driver-specific options:
* - timeout [int] (in seconds) [mssql-only]
* - analyze [boolean] [pgsql and mysql]
* - full [boolean] [pgsql-only]
* - freeze [boolean] [pgsql-only]
*
* @return mixed MDB2_OK success, a MDB2 error on failure
* @access public
*/
function vacuum($table = null, $options = array())
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'VACUUM';
if (!empty($options['full'])) {
$query .= ' FULL';
}
if (!empty($options['freeze'])) {
$query .= ' FREEZE';
}
if (!empty($options['analyze'])) {
$query .= ' ANALYZE';
}
if (!empty($table)) {
$query .= ' '.$db->quoteIdentifier($table, true);
}
return $db->exec($query);
}
// }}}
// {{{ alterTable()
/**
* alter an existing table
*
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @access public
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function alterTable($name, $changes, $check)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
foreach ($changes as $change_name => $change) {
switch ($change_name) {
case 'add':
case 'remove':
case 'change':
case 'name':
case 'rename':
break;
default:
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'change type "'.$change_name.'\" not yet supported', __FUNCTION__);
}
}
if ($check) {
return MDB2_OK;
}
$name = $db->quoteIdentifier($name, true);
if (!empty($changes['remove']) && is_array($changes['remove'])) {
foreach ($changes['remove'] as $field_name => $field) {
$field_name = $db->quoteIdentifier($field_name, true);
$query = 'DROP ' . $field_name;
$result = $db->exec("ALTER TABLE $name $query");
if (PEAR::isError($result)) {
return $result;
}
}
}
if (!empty($changes['rename']) && is_array($changes['rename'])) {
foreach ($changes['rename'] as $field_name => $field) {
$field_name = $db->quoteIdentifier($field_name, true);
$result = $db->exec("ALTER TABLE $name RENAME COLUMN $field_name TO ".$db->quoteIdentifier($field['name'], true));
if (PEAR::isError($result)) {
return $result;
}
}
}
if (!empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $field_name => $field) {
$query = 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
$result = $db->exec("ALTER TABLE $name $query");
if (PEAR::isError($result)) {
return $result;
}
}
}
if (!empty($changes['change']) && is_array($changes['change'])) {
foreach ($changes['change'] as $field_name => $field) {
$field_name = $db->quoteIdentifier($field_name, true);
if (!empty($field['definition']['type'])) {
$server_info = $db->getServerVersion();
if (PEAR::isError($server_info)) {
return $server_info;
}
if (is_array($server_info) && $server_info['major'] < 8) {
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'changing column type for "'.$change_name.'\" requires PostgreSQL 8.0 or above', __FUNCTION__);
}
$db->loadModule('Datatype', null, true);
$type = $db->datatype->getTypeDeclaration($field['definition']);
$query = "ALTER $field_name TYPE $type USING CAST($field_name AS $type)";
$result = $db->exec("ALTER TABLE $name $query");
if (PEAR::isError($result)) {
return $result;
}
}
if (array_key_exists('default', $field['definition'])) {
$query = "ALTER $field_name SET DEFAULT ".$db->quote($field['definition']['default'], $field['definition']['type']);
$result = $db->exec("ALTER TABLE $name $query");
if (PEAR::isError($result)) {
return $result;
}
}
if (!empty($field['definition']['notnull'])) {
$query = "ALTER $field_name ".($field['definition']['notnull'] ? 'SET' : 'DROP').' NOT NULL';
$result = $db->exec("ALTER TABLE $name $query");
if (PEAR::isError($result)) {
return $result;
}
}
}
}
if (!empty($changes['name'])) {
$change_name = $db->quoteIdentifier($changes['name'], true);
$result = $db->exec("ALTER TABLE $name RENAME TO ".$change_name);
if (PEAR::isError($result)) {
return $result;
}
}
return MDB2_OK;
}
// }}}
// {{{ listDatabases()
/**
* list all databases
*
* @return mixed array of database names on success, a MDB2 error on failure
* @access public
*/
function listDatabases()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SELECT datname FROM pg_database';
$result2 = $db->standaloneQuery($query, array('text'), false);
if (!MDB2::isResultCommon($result2)) {
return $result2;
}
$result = $result2->fetchCol();
$result2->free();
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listUsers()
/**
* list all users
*
* @return mixed array of user names on success, a MDB2 error on failure
* @access public
*/
function listUsers()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SELECT usename FROM pg_user';
$result2 = $db->standaloneQuery($query, array('text'), false);
if (!MDB2::isResultCommon($result2)) {
return $result2;
}
$result = $result2->fetchCol();
$result2->free();
return $result;
}
// }}}
// {{{ listViews()
/**
* list all views in the current database
*
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listViews()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT viewname
FROM pg_views
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
AND viewname !~ '^pg_'";
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableViews()
/**
* list the views in the database that reference a given table
*
* @param string table for which all referenced views should be found
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listTableViews($table)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SELECT viewname FROM pg_views NATURAL JOIN pg_tables';
$query.= ' WHERE tablename ='.$db->quote($table, 'text');
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listFunctions()
/**
* list all functions in the current database
*
* @return mixed array of function names on success, a MDB2 error on failure
* @access public
*/
function listFunctions()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "
SELECT
proname
FROM
pg_proc pr,
pg_type tp
WHERE
tp.oid = pr.prorettype
AND pr.proisagg = FALSE
AND tp.typname <> 'trigger'
AND pr.pronamespace IN
(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableTriggers()
/**
* list all triggers in the database that reference a given table
*
* @param string table for which all referenced triggers should be found
* @return mixed array of trigger names on success, a MDB2 error on failure
* @access public
*/
function listTableTriggers($table = null)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SELECT trg.tgname AS trigger_name
FROM pg_trigger trg,
pg_class tbl
WHERE trg.tgrelid = tbl.oid';
if (!is_null($table)) {
$table = $db->quote(strtoupper($table), 'text');
$query .= " AND tbl.relname = $table";
}
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTables()
/**
* list all tables in the current database
*
* @return mixed array of table names on success, a MDB2 error on failure
* @access public
*/
function listTables()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
// gratuitously stolen from PEAR DB _getSpecialQuery in pgsql.php
$query = 'SELECT c.relname AS "Name"'
. ' FROM pg_class c, pg_user u'
. ' WHERE c.relowner = u.usesysid'
. " AND c.relkind = 'r'"
. ' AND NOT EXISTS'
. ' (SELECT 1 FROM pg_views'
. ' WHERE viewname = c.relname)'
. " AND c.relname !~ '^(pg_|sql_)'"
. ' UNION'
. ' SELECT c.relname AS "Name"'
. ' FROM pg_class c'
. " WHERE c.relkind = 'r'"
. ' AND NOT EXISTS'
. ' (SELECT 1 FROM pg_views'
. ' WHERE viewname = c.relname)'
. ' AND NOT EXISTS'
. ' (SELECT 1 FROM pg_user'
. ' WHERE usesysid = c.relowner)'
. " AND c.relname !~ '^pg_'";
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableFields()
/**
* list all fields in a table in the current database
*
* @param string $table name of table that should be used in method
* @return mixed array of field names on success, a MDB2 error on failure
* @access public
*/
function listTableFields($table)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table);
$table = $db->quoteIdentifier($table, true);
if (!empty($schema)) {
$table = $db->quoteIdentifier($schema, true) . '.' .$table;
}
$db->setLimit(1);
$result2 = $db->query("SELECT * FROM $table");
if (PEAR::isError($result2)) {
return $result2;
}
$result = $result2->getColumnNames();
$result2->free();
if (PEAR::isError($result)) {
return $result;
}
return array_flip($result);
}
// }}}
// {{{ listTableIndexes()
/**
* list all indexes in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of index names on success, a MDB2 error on failure
* @access public
*/
function listTableIndexes($table)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table);
$table = $db->quote($table, 'text');
$subquery = "SELECT indexrelid
FROM pg_index
LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
WHERE pg_class.relname = $table
AND indisunique != 't'
AND indisprimary != 't'";
if (!empty($schema)) {
$subquery .= ' AND pg_namespace.nspname = '.$db->quote($schema, 'text');
}
$query = "SELECT relname FROM pg_class WHERE oid IN ($subquery)";
$indexes = $db->queryCol($query, 'text');
if (PEAR::isError($indexes)) {
return $indexes;
}
$result = array();
foreach ($indexes as $index) {
$index = $this->_fixIndexName($index);
if (!empty($index)) {
$result[$index] = true;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ dropConstraint()
/**
* drop existing constraint
*
* @param string $table name of table that should be used in method
* @param string $name name of the constraint to be dropped
* @param string $primary hint if the constraint is primary
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropConstraint($table, $name, $primary = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
// is it an UNIQUE index?
$query = 'SELECT relname
FROM pg_class
WHERE oid IN (
SELECT indexrelid
FROM pg_index, pg_class
WHERE pg_class.relname = '.$db->quote($table, 'text').'
AND pg_class.oid = pg_index.indrelid
AND indisunique = \'t\')
EXCEPT
SELECT conname
FROM pg_constraint, pg_class
WHERE pg_constraint.conrelid = pg_class.oid
AND relname = '. $db->quote($table, 'text');
$unique = $db->queryCol($query, 'text');
if (PEAR::isError($unique) || empty($unique)) {
// not an UNIQUE index, maybe a CONSTRAINT
return parent::dropConstraint($table, $name, $primary);
}
if (in_array($name, $unique)) {
return $db->exec('DROP INDEX '.$db->quoteIdentifier($name, true));
}
$idxname = $db->getIndexName($name);
if (in_array($idxname, $unique)) {
return $db->exec('DROP INDEX '.$db->quoteIdentifier($idxname, true));
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$name . ' is not an existing constraint for table ' . $table, __FUNCTION__);
}
// }}}
// {{{ listTableConstraints()
/**
* list all constraints in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of constraint names on success, a MDB2 error on failure
* @access public
*/
function listTableConstraints($table)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table);
$table = $db->quote($table, 'text');
$query = 'SELECT conname
FROM pg_constraint
LEFT JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
WHERE relname = ' .$table;
if (!empty($schema)) {
$query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
}
$query .= '
UNION DISTINCT
SELECT relname
FROM pg_class
WHERE oid IN (
SELECT indexrelid
FROM pg_index
LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
WHERE pg_class.relname = '.$table.'
AND indisunique = \'t\'';
if (!empty($schema)) {
$query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
}
$query .= ')';
$constraints = $db->queryCol($query);
if (PEAR::isError($constraints)) {
return $constraints;
}
$result = array();
foreach ($constraints as $constraint) {
$constraint = $this->_fixIndexName($constraint);
if (!empty($constraint)) {
$result[$constraint] = true;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
&& $db->options['field_case'] == CASE_LOWER
) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ createSequence()
/**
* create sequence
*
* @param string $seq_name name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createSequence($seq_name, $start = 1)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
return $db->exec("CREATE SEQUENCE $sequence_name INCREMENT 1".
($start < 1 ? " MINVALUE $start" : '')." START $start");
}
// }}}
// {{{ dropSequence()
/**
* drop existing sequence
*
* @param string $seq_name name of the sequence to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropSequence($seq_name)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
return $db->exec("DROP SEQUENCE $sequence_name");
}
// }}}
// {{{ listSequences()
/**
* list all sequences in the current database
*
* @return mixed array of sequence names on success, a MDB2 error on failure
* @access public
*/
function listSequences()
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT relname FROM pg_class WHERE relkind = 'S' AND relnamespace IN";
$query.= "(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
$table_names = $db->queryCol($query);
if (PEAR::isError($table_names)) {
return $table_names;
}
$result = array();
foreach ($table_names as $table_name) {
$result[] = $this->_fixSequenceName($table_name);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id: sqlite.php,v 1.76 2008/05/31 11:48:48 quipo Exp $
//
require_once('MDB2/Driver/Manager/Common.php');
/**
* MDB2 SQLite driver for the management modules
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Manager_sqlite extends MDB2_Driver_Manager_Common
{
// {{{ createDatabase()
/**
* create a new database
*
* @param string $name name of the database that should be created
* @param array $options array with charset info
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createDatabase($name, $options = array())
{
$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" );
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$database_file = $db->_getDatabaseFile($name);
if (file_exists($database_file)) {
return $db->raiseError(MDB2_ERROR_ALREADY_EXISTS, null, null,
'database already exists', __FUNCTION__);
}
$php_errormsg = '';
$database_file="$datadir/$database_file.db";
$handle = sqlite_open($database_file, $db->dsn['mode'], $php_errormsg);
if (!$handle) {
return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
(isset($php_errormsg) ? $php_errormsg : 'could not create the database file'), __FUNCTION__);
}
if (!empty($options['charset'])) {
$query = 'PRAGMA encoding = ' . $db->quote($options['charset'], 'text');
@sqlite_query($query, $handle);
}
@sqlite_close($handle);
return MDB2_OK;
}
// }}}
// {{{ dropDatabase()
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropDatabase($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$database_file = $db->_getDatabaseFile($name);
if (!@file_exists($database_file)) {
return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null,
'database does not exist', __FUNCTION__);
}
$result = @unlink($database_file);
if (!$result) {
return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null,
(isset($php_errormsg) ? $php_errormsg : 'could not remove the database file'), __FUNCTION__);
}
return MDB2_OK;
}
// }}}
// {{{ _getAdvancedFKOptions()
/**
* Return the FOREIGN KEY query section dealing with non-standard options
* as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
*
* @param array $definition
* @return string
* @access protected
*/
function _getAdvancedFKOptions($definition)
{
$query = '';
if (!empty($definition['match'])) {
$query .= ' MATCH '.$definition['match'];
}
if (!empty($definition['onupdate']) && (strtoupper($definition['onupdate']) != 'NO ACTION')) {
$query .= ' ON UPDATE '.$definition['onupdate'];
}
if (!empty($definition['ondelete']) && (strtoupper($definition['ondelete']) != 'NO ACTION')) {
$query .= ' ON DELETE '.$definition['ondelete'];
}
if (!empty($definition['deferrable'])) {
$query .= ' DEFERRABLE';
} else {
$query .= ' NOT DEFERRABLE';
}
if (!empty($definition['initiallydeferred'])) {
$query .= ' INITIALLY DEFERRED';
} else {
$query .= ' INITIALLY IMMEDIATE';
}
return $query;
}
// }}}
// {{{ _getCreateTableQuery()
/**
* Create a basic SQL query for a new table creation
* @param string $name Name of the database that should be created
* @param array $fields Associative array that contains the definition of each field of the new table
* @param array $options An associative array of table options
* @return mixed string (the SQL query) on success, a MDB2 error on failure
* @see createTable()
*/
function _getCreateTableQuery($name, $fields, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!$name) {
return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
'no valid table name specified', __FUNCTION__);
}
if (empty($fields)) {
return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
'no fields specified for table "'.$name.'"', __FUNCTION__);
}
$query_fields = $this->getFieldDeclarationList($fields);
if (PEAR::isError($query_fields)) {
return $query_fields;
}
if (!empty($options['primary'])) {
$query_fields.= ', PRIMARY KEY ('.implode(', ', array_keys($options['primary'])).')';
}
if (!empty($options['foreign_keys'])) {
foreach ($options['foreign_keys'] as $fkname => $fkdef) {
if (empty($fkdef)) {
continue;
}
$query_fields.= ', CONSTRAINT '.$fkname.' FOREIGN KEY ('.implode(', ', array_keys($fkdef['fields'])).')';
$query_fields.= ' REFERENCES '.$fkdef['references']['table'].' ('.implode(', ', array_keys($fkdef['references']['fields'])).')';
$query_fields.= $this->_getAdvancedFKOptions($fkdef);
}
}
$name = $db->quoteIdentifier($name, true);
$result = 'CREATE ';
if (!empty($options['temporary'])) {
$result .= $this->_getTemporaryTableQuery();
}
$result .= " TABLE $name ($query_fields)";
return $result;
}
// }}}
// {{{ createTable()
/**
* create a new table
*
* @param string $name Name of the database that should be created
* @param array $fields Associative array that contains the definition
* of each field of the new table
* @param array $options An associative array of table options
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createTable($name, $fields, $options = array())
{
$result = parent::createTable($name, $fields, $options);
if (PEAR::isError($result)) {
return $result;
}
// create triggers to enforce FOREIGN KEY constraints
if (!empty($options['foreign_keys'])) {
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
foreach ($options['foreign_keys'] as $fkname => $fkdef) {
if (empty($fkdef)) {
continue;
}
//set actions to default if not set
$fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']);
$fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']);
$trigger_names = array(
'insert' => $fkname.'_insert_trg',
'update' => $fkname.'_update_trg',
'pk_update' => $fkname.'_pk_update_trg',
'pk_delete' => $fkname.'_pk_delete_trg',
);
//create the [insert|update] triggers on the FK table
$table_fields = array_keys($fkdef['fields']);
$referenced_fields = array_keys($fkdef['references']['fields']);
$query = 'CREATE TRIGGER %s BEFORE %s ON '.$name
.' FOR EACH ROW BEGIN'
.' SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')'
.' WHERE (SELECT ';
$aliased_fields = array();
foreach ($referenced_fields as $field) {
$aliased_fields[] = $fkdef['references']['table'] .'.'.$field .' AS '.$field;
}
$query .= implode(',', $aliased_fields)
.' FROM '.$fkdef['references']['table']
.' WHERE ';
$conditions = array();
for ($i=0; $i<count($table_fields); $i++) {
$conditions[] = $referenced_fields[$i] .' = NEW.'.$table_fields[$i];
}
$query .= implode(' AND ', $conditions).') IS NULL; END;';
$result = $db->exec(sprintf($query, $trigger_names['insert'], 'INSERT', 'insert'));
if (PEAR::isError($result)) {
return $result;
}
$result = $db->exec(sprintf($query, $trigger_names['update'], 'UPDATE', 'update'));
if (PEAR::isError($result)) {
return $result;
}
//create the ON [UPDATE|DELETE] triggers on the primary table
$restrict_action = 'SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')'
.' WHERE (SELECT ';
$aliased_fields = array();
foreach ($table_fields as $field) {
$aliased_fields[] = $name .'.'.$field .' AS '.$field;
}
$restrict_action .= implode(',', $aliased_fields)
.' FROM '.$name
.' WHERE ';
$conditions = array();
$new_values = array();
$null_values = array();
for ($i=0; $i<count($table_fields); $i++) {
$conditions[] = $table_fields[$i] .' = OLD.'.$referenced_fields[$i];
$new_values[] = $table_fields[$i] .' = NEW.'.$referenced_fields[$i];
$null_values[] = $table_fields[$i] .' = NULL';
}
$conditions2 = array();
for ($i=0; $i<count($referenced_fields); $i++) {
$conditions2[] = 'NEW.'.$referenced_fields[$i] .' <> OLD.'.$referenced_fields[$i];
}
$restrict_action .= implode(' AND ', $conditions).') IS NOT NULL'
.' AND (' .implode(' OR ', $conditions2) .')';
$cascade_action_update = 'UPDATE '.$name.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions);
$cascade_action_delete = 'DELETE FROM '.$name.' WHERE '.implode(' AND ', $conditions);
$setnull_action = 'UPDATE '.$name.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions);
if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) {
$db->loadModule('Reverse', null, true);
$default_values = array();
foreach ($table_fields as $table_field) {
$field_definition = $db->reverse->getTableFieldDefinition($name, $field);
if (PEAR::isError($field_definition)) {
return $field_definition;
}
$default_values[] = $table_field .' = '. $field_definition[0]['default'];
}
$setdefault_action = 'UPDATE '.$name.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions);
}
$query = 'CREATE TRIGGER %s'
.' %s ON '.$fkdef['references']['table']
.' FOR EACH ROW BEGIN ';
if ('CASCADE' == $fkdef['onupdate']) {
$sql_update = sprintf($query, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . $cascade_action_update. '; END;';
} elseif ('SET NULL' == $fkdef['onupdate']) {
$sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action. '; END;';
} elseif ('SET DEFAULT' == $fkdef['onupdate']) {
$sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action. '; END;';
} elseif ('NO ACTION' == $fkdef['onupdate']) {
$sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . '; END;';
} elseif ('RESTRICT' == $fkdef['onupdate']) {
$sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . '; END;';
}
if ('CASCADE' == $fkdef['ondelete']) {
$sql_delete = sprintf($query, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . $cascade_action_delete. '; END;';
} elseif ('SET NULL' == $fkdef['ondelete']) {
$sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action. '; END;';
} elseif ('SET DEFAULT' == $fkdef['ondelete']) {
$sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action. '; END;';
} elseif ('NO ACTION' == $fkdef['ondelete']) {
$sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . '; END;';
} elseif ('RESTRICT' == $fkdef['ondelete']) {
$sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . '; END;';
}
if (PEAR::isError($result)) {
return $result;
}
$result = $db->exec($sql_delete);
if (PEAR::isError($result)) {
return $result;
}
$result = $db->exec($sql_update);
if (PEAR::isError($result)) {
return $result;
}
}
}
if (PEAR::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ dropTable()
/**
* drop an existing table
*
* @param string $name name of the table that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropTable($name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
//delete the triggers associated to existing FK constraints
$constraints = $this->listTableConstraints($name);
if (!PEAR::isError($constraints) && !empty($constraints)) {
$db->loadModule('Reverse', null, true);
foreach ($constraints as $constraint) {
$definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
$result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']);
if (PEAR::isError($result)) {
return $result;
}
}
}
}
$name = $db->quoteIdentifier($name, true);
return $db->exec("DROP TABLE $name");
}
// }}}
// {{{ vacuum()
/**
* Optimize (vacuum) all the tables in the db (or only the specified table)
* and optionally run ANALYZE.
*
* @param string $table table name (all the tables if empty)
* @param array $options an array with driver-specific options:
* - timeout [int] (in seconds) [mssql-only]
* - analyze [boolean] [pgsql and mysql]
* - full [boolean] [pgsql-only]
* - freeze [boolean] [pgsql-only]
*
* @return mixed MDB2_OK success, a MDB2 error on failure
* @access public
*/
function vacuum($table = null, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'VACUUM';
if (!empty($table)) {
$query .= ' '.$db->quoteIdentifier($table, true);
}
return $db->exec($query);
}
// }}}
// {{{ alterTable()
/**
* alter an existing table
*
* @param string $name name of the table that is intended to be changed.
* @param array $changes associative array that contains the details of each type
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the MDB2 parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the MDB2 parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @param boolean $check indicates whether the function should just check if the DBMS driver
* can perform the requested table alterations if the value is true or
* actually perform them otherwise.
* @access public
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function alterTable($name, $changes, $check, $options = array())
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
foreach ($changes as $change_name => $change) {
switch ($change_name) {
case 'add':
case 'remove':
case 'change':
case 'name':
case 'rename':
break;
default:
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'change type "'.$change_name.'" not yet supported', __FUNCTION__);
}
}
if ($check) {
return MDB2_OK;
}
$db->loadModule('Reverse', null, true);
// actually sqlite 2.x supports no ALTER TABLE at all .. so we emulate it
$fields = $db->manager->listTableFields($name);
if (PEAR::isError($fields)) {
return $fields;
}
$fields = array_flip($fields);
foreach ($fields as $field => $value) {
$definition = $db->reverse->getTableFieldDefinition($name, $field);
if (PEAR::isError($definition)) {
return $definition;
}
$fields[$field] = $definition[0];
}
$indexes = $db->manager->listTableIndexes($name);
if (PEAR::isError($indexes)) {
return $indexes;
}
$indexes = array_flip($indexes);
foreach ($indexes as $index => $value) {
$definition = $db->reverse->getTableIndexDefinition($name, $index);
if (PEAR::isError($definition)) {
return $definition;
}
$indexes[$index] = $definition;
}
$constraints = $db->manager->listTableConstraints($name);
if (PEAR::isError($constraints)) {
return $constraints;
}
if (!array_key_exists('foreign_keys', $options)) {
$options['foreign_keys'] = array();
}
$constraints = array_flip($constraints);
foreach ($constraints as $constraint => $value) {
if (!empty($definition['primary'])) {
if (!array_key_exists('primary', $options)) {
$options['primary'] = $definition['fields'];
//remove from the $constraint array, it's already handled by createTable()
unset($constraints[$constraint]);
}
} else {
$c_definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
if (PEAR::isError($c_definition)) {
return $c_definition;
}
if (!empty($c_definition['foreign'])) {
if (!array_key_exists($constraint, $options['foreign_keys'])) {
$options['foreign_keys'][$constraint] = $c_definition;
}
//remove from the $constraint array, it's already handled by createTable()
unset($constraints[$constraint]);
} else {
$constraints[$constraint] = $c_definition;
}
}
}
$name_new = $name;
$create_order = $select_fields = array_keys($fields);
foreach ($changes as $change_name => $change) {
switch ($change_name) {
case 'add':
foreach ($change as $field_name => $field) {
$fields[$field_name] = $field;
$create_order[] = $field_name;
}
break;
case 'remove':
foreach ($change as $field_name => $field) {
unset($fields[$field_name]);
$select_fields = array_diff($select_fields, array($field_name));
$create_order = array_diff($create_order, array($field_name));
}
break;
case 'change':
foreach ($change as $field_name => $field) {
$fields[$field_name] = $field['definition'];
}
break;
case 'name':
$name_new = $change;
break;
case 'rename':
foreach ($change as $field_name => $field) {
unset($fields[$field_name]);
$fields[$field['name']] = $field['definition'];
$create_order[array_search($field_name, $create_order)] = $field['name'];
}
break;
default:
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'change type "'.$change_name.'" not yet supported', __FUNCTION__);
}
}
$data = null;
if (!empty($select_fields)) {
$query = 'SELECT '.implode(', ', $select_fields).' FROM '.$db->quoteIdentifier($name, true);
$data = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED);
}
$result = $this->dropTable($name);
if (PEAR::isError($result)) {
return $result;
}
$result = $this->createTable($name_new, $fields, $options);
if (PEAR::isError($result)) {
return $result;
}
foreach ($indexes as $index => $definition) {
$this->createIndex($name_new, $index, $definition);
}
foreach ($constraints as $constraint => $definition) {
$this->createConstraint($name_new, $constraint, $definition);
}
if (!empty($select_fields) && !empty($data)) {
$query = 'INSERT INTO '.$db->quoteIdentifier($name_new, true);
$query.= '('.implode(', ', array_slice(array_keys($fields), 0, count($select_fields))).')';
$query.=' VALUES (?'.str_repeat(', ?', (count($select_fields) - 1)).')';
$stmt =$db->prepare($query, null, MDB2_PREPARE_MANIP);
if (PEAR::isError($stmt)) {
return $stmt;
}
foreach ($data as $row) {
$result = $stmt->execute($row);
if (PEAR::isError($result)) {
return $result;
}
}
}
return MDB2_OK;
}
// }}}
// {{{ listDatabases()
/**
* list all databases
*
* @return mixed array of database names on success, a MDB2 error on failure
* @access public
*/
function listDatabases()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'list databases is not supported', __FUNCTION__);
}
// }}}
// {{{ listUsers()
/**
* list all users
*
* @return mixed array of user names on success, a MDB2 error on failure
* @access public
*/
function listUsers()
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'list databases is not supported', __FUNCTION__);
}
// }}}
// {{{ listViews()
/**
* list all views in the current database
*
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listViews($dummy=null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT name FROM sqlite_master WHERE type='view' AND sql NOT NULL";
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableViews()
/**
* list the views in the database that reference a given table
*
* @param string table for which all referenced views should be found
* @return mixed array of view names on success, a MDB2 error on failure
* @access public
*/
function listTableViews($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
$views = $db->queryAll($query, array('text', 'text'), MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($views)) {
return $views;
}
$result = array();
foreach ($views as $row) {
if (preg_match("/^create view .* \bfrom\b\s+\b{$table}\b /i", $row['sql'])) {
if (!empty($row['name'])) {
$result[$row['name']] = true;
}
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ listTables()
/**
* list all tables in the current database
*
* @return mixed array of table names on success, a MDB2 error on failure
* @access public
*/
function listTables($dummy=null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name";
$table_names = $db->queryCol($query);
if (PEAR::isError($table_names)) {
return $table_names;
}
$result = array();
foreach ($table_names as $table_name) {
if (!$this->_fixSequenceName($table_name, true)) {
$result[] = $table_name;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ listTableFields()
/**
* list all fields in a table in the current database
*
* @param string $table name of table that should be used in method
* @return mixed array of field names on success, a MDB2 error on failure
* @access public
*/
function listTableFields($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$result = $db->loadModule('Reverse', null, true);
if (PEAR::isError($result)) {
return $result;
}
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= 'name='.$db->quote($table, 'text');
}
$sql = $db->queryOne($query);
if (PEAR::isError($sql)) {
return $sql;
}
$columns = $db->reverse->_getTableColumns($sql);
$fields = array();
foreach ($columns as $column) {
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
$fields[] = $column['name'];
}
return $fields;
}
// }}}
// {{{ listTableTriggers()
/**
* list all triggers in the database that reference a given table
*
* @param string table for which all referenced triggers should be found
* @return mixed array of trigger names on success, a MDB2 error on failure
* @access public
*/
function listTableTriggers($table = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT name FROM sqlite_master WHERE type='trigger' AND sql NOT NULL";
if (!is_null($table)) {
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= ' AND LOWER(tbl_name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= ' AND tbl_name='.$db->quote($table, 'text');
}
}
$result = $db->queryCol($query);
if (PEAR::isError($result)) {
return $result;
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
// {{{ createIndex()
/**
* Get the stucture of a field into an array
*
* @param string $table name of the table on which the index is to be created
* @param string $name name of the index to be created
* @param array $definition associative array that defines properties of the index to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the index fields as array
* indexes. Each entry of this array is set to another type of associative
* array that specifies properties of the index that are specific to
* each field.
*
* Currently, only the sorting property is supported. It should be used
* to define the sorting direction of the index. It may be set to either
* ascending or descending.
*
* Not all DBMS support index sorting direction configuration. The DBMS
* drivers of those that do not support it ignore this property. Use the
* function support() to determine whether the DBMS driver can manage indexes.
* Example
* array(
* 'fields' => array(
* 'user_name' => array(
* 'sorting' => 'ascending'
* ),
* 'last_login' => array()
* )
* )
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createIndex($table, $name, $definition)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quoteIdentifier($table, true);
$name = $db->getIndexName($name);
$query = "CREATE INDEX $name ON $table";
$fields = array();
foreach ($definition['fields'] as $field_name => $field) {
$field_string = $field_name;
if (!empty($field['sorting'])) {
switch ($field['sorting']) {
case 'ascending':
$field_string.= ' ASC';
break;
case 'descending':
$field_string.= ' DESC';
break;
}
}
$fields[] = $field_string;
}
$query .= ' ('.implode(', ', $fields) . ')';
return $db->exec($query);
}
// }}}
// {{{ dropIndex()
/**
* drop existing index
*
* @param string $table name of table that should be used in method
* @param string $name name of the index to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropIndex($table, $name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$name = $db->getIndexName($name);
return $db->exec("DROP INDEX $name");
}
// }}}
// {{{ listTableIndexes()
/**
* list all indexes in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of index names on success, a MDB2 error on failure
* @access public
*/
function listTableIndexes($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quote($table, 'text');
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(tbl_name)='.strtolower($table);
} else {
$query.= "tbl_name=$table";
}
$query.= " AND sql NOT NULL ORDER BY name";
$indexes = $db->queryCol($query, 'text');
if (PEAR::isError($indexes)) {
return $indexes;
}
$result = array();
foreach ($indexes as $sql) {
if (preg_match("/^create index ([^ ]+) on /i", $sql, $tmp)) {
$index = $this->_fixIndexName($tmp[1]);
if (!empty($index)) {
$result[$index] = true;
}
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ createConstraint()
/**
* create a constraint on a table
*
* @param string $table name of the table on which the constraint is to be created
* @param string $name name of the constraint to be created
* @param array $definition associative array that defines properties of the constraint to be created.
* Currently, only one property named FIELDS is supported. This property
* is also an associative with the names of the constraint fields as array
* constraints. Each entry of this array is set to another type of associative
* array that specifies properties of the constraint that are specific to
* each field.
*
* Example
* array(
* 'fields' => array(
* 'user_name' => array(),
* 'last_login' => array()
* )
* )
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createConstraint($table, $name, $definition)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!empty($definition['primary'])) {
return $db->manager->alterTable($table, array(), false, array('primary' => $definition['fields']));
}
if (!empty($definition['foreign'])) {
return $db->manager->alterTable($table, array(), false, array('foreign_keys' => array($name => $definition)));
}
$table = $db->quoteIdentifier($table, true);
$name = $db->getIndexName($name);
$query = "CREATE UNIQUE INDEX $name ON $table";
$fields = array();
foreach ($definition['fields'] as $field_name => $field) {
$field_string = $field_name;
if (!empty($field['sorting'])) {
switch ($field['sorting']) {
case 'ascending':
$field_string.= ' ASC';
break;
case 'descending':
$field_string.= ' DESC';
break;
}
}
$fields[] = $field_string;
}
$query .= ' ('.implode(', ', $fields) . ')';
return $db->exec($query);
}
// }}}
// {{{ dropConstraint()
/**
* drop existing constraint
*
* @param string $table name of table that should be used in method
* @param string $name name of the constraint to be dropped
* @param string $primary hint if the constraint is primary
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropConstraint($table, $name, $primary = false)
{
if ($primary || $name == 'PRIMARY') {
return $this->alterTable($table, array(), false, array('primary' => null));
}
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
//is it a FK constraint? If so, also delete the associated triggers
$db->loadModule('Reverse', null, true);
$definition = $db->reverse->getTableConstraintDefinition($table, $name);
if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
//first drop the FK enforcing triggers
$result = $this->_dropFKTriggers($table, $name, $definition['references']['table']);
if (PEAR::isError($result)) {
return $result;
}
//then drop the constraint itself
return $this->alterTable($table, array(), false, array('foreign_keys' => array($name => null)));
}
$name = $db->getIndexName($name);
return $db->exec("DROP INDEX $name");
}
// }}}
// {{{ _dropFKTriggers()
/**
* Drop the triggers created to enforce the FOREIGN KEY constraint on the table
*
* @param string $table table name
* @param string $fkname FOREIGN KEY constraint name
* @param string $referenced_table referenced table name
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access private
*/
function _dropFKTriggers($table, $fkname, $referenced_table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$triggers = $this->listTableTriggers($table);
$triggers2 = $this->listTableTriggers($referenced_table);
if (!PEAR::isError($triggers2) && !PEAR::isError($triggers)) {
$triggers = array_merge($triggers, $triggers2);
$pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i';
foreach ($triggers as $trigger) {
if (preg_match($pattern, $trigger)) {
$result = $db->exec('DROP TRIGGER '.$trigger);
if (PEAR::isError($result)) {
return $result;
}
}
}
}
return MDB2_OK;
}
// }}}
// {{{ listTableConstraints()
/**
* list all constraints in a table
*
* @param string $table name of table that should be used in method
* @return mixed array of constraint names on success, a MDB2 error on failure
* @access public
*/
function listTableConstraints($table)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$table = $db->quote($table, 'text');
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(tbl_name)='.strtolower($table);
} else {
$query.= "tbl_name=$table";
}
$query.= " AND sql NOT NULL ORDER BY name";
$indexes = $db->queryCol($query, 'text');
if (PEAR::isError($indexes)) {
return $indexes;
}
$result = array();
foreach ($indexes as $sql) {
if (preg_match("/^create unique index ([^ ]+) on /i", $sql, $tmp)) {
$index = $this->_fixIndexName($tmp[1]);
if (!empty($index)) {
$result[$index] = true;
}
}
}
// also search in table definition for PRIMARY KEYs...
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.strtolower($table);
} else {
$query.= "name=$table";
}
$query.= " AND sql NOT NULL ORDER BY name";
$table_def = $db->queryOne($query, 'text');
if (PEAR::isError($table_def)) {
return $table_def;
}
if (preg_match("/\bPRIMARY\s+KEY\b/i", $table_def, $tmp)) {
$result['primary'] = true;
}
// ...and for FOREIGN KEYs
if (preg_match_all("/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN\s+KEY/imsx", $table_def, $tmp)) {
foreach ($tmp[1] as $fk) {
$result[$fk] = true;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_change_key_case($result, $db->options['field_case']);
}
return array_keys($result);
}
// }}}
// {{{ createSequence()
/**
* create sequence
*
* @param string $seq_name name of the sequence to be created
* @param string $start start value of the sequence; default is 1
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function createSequence($seq_name, $start = 1)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
$seqcol_name = $db->quoteIdentifier($db->options['seqcol_name'], true);
$query = "CREATE TABLE $sequence_name ($seqcol_name INTEGER PRIMARY KEY DEFAULT 0 NOT NULL)";
$res = $db->exec($query);
if (PEAR::isError($res)) {
return $res;
}
if ($start == 1) {
return MDB2_OK;
}
$res = $db->exec("INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')');
if (!PEAR::isError($res)) {
return MDB2_OK;
}
// Handle error
$result = $db->exec("DROP TABLE $sequence_name");
if (PEAR::isError($result)) {
return $db->raiseError($result, null, null,
'could not drop inconsistent sequence table', __FUNCTION__);
}
return $db->raiseError($res, null, null,
'could not create sequence table', __FUNCTION__);
}
// }}}
// {{{ dropSequence()
/**
* drop existing sequence
*
* @param string $seq_name name of the sequence to be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function dropSequence($seq_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
return $db->exec("DROP TABLE $sequence_name");
}
// }}}
// {{{ listSequences()
/**
* list all sequences in the current database
*
* @return mixed array of sequence names on success, a MDB2 error on failure
* @access public
*/
function listSequences($dummy=null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name";
$table_names = $db->queryCol($query);
if (PEAR::isError($table_names)) {
return $table_names;
}
$result = array();
foreach ($table_names as $table_name) {
if ($sqn = $this->_fixSequenceName($table_name, true)) {
$result[] = $sqn;
}
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
}
return $result;
}
// }}}
}
?>
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: mysql.php,v 1.214 2008/11/16 21:45:08 quipo Exp $
//
/**
* MDB2 MySQL driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_mysql extends MDB2_Driver_Common
{
// {{{ properties
var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => '\\', 'escape_pattern' => '\\');
var $identifier_quoting = array('start' => '`', 'end' => '`', 'escape' => '`');
var $sql_comments = array(
array('start' => '-- ', 'end' => "\n", 'escape' => false),
array('start' => '#', 'end' => "\n", 'escape' => false),
array('start' => '/*', 'end' => '*/', 'escape' => false),
);
var $server_capabilities_checked = false;
var $start_transaction = false;
var $varchar_max_length = 255;
// }}}
// {{{ constructor
/**
* Constructor
*/
function __construct()
{
parent::__construct();
$this->phptype = 'mysql';
$this->dbsyntax = 'mysql';
$this->supported['sequences'] = 'emulated';
$this->supported['indexes'] = true;
$this->supported['affected_rows'] = true;
$this->supported['transactions'] = false;
$this->supported['savepoints'] = false;
$this->supported['summary_functions'] = true;
$this->supported['order_by_text'] = true;
$this->supported['current_id'] = 'emulated';
$this->supported['limit_queries'] = true;
$this->supported['LOBs'] = true;
$this->supported['replace'] = true;
$this->supported['sub_selects'] = 'emulated';
$this->supported['triggers'] = false;
$this->supported['auto_increment'] = true;
$this->supported['primary_key'] = true;
$this->supported['result_introspection'] = true;
$this->supported['prepared_statements'] = 'emulated';
$this->supported['identifier_quoting'] = true;
$this->supported['pattern_escaping'] = true;
$this->supported['new_link'] = true;
$this->options['DBA_username'] = false;
$this->options['DBA_password'] = false;
$this->options['default_table_type'] = '';
$this->options['max_identifiers_length'] = 64;
$this->_reCheckSupportedOptions();
}
// }}}
// {{{ _reCheckSupportedOptions()
/**
* If the user changes certain options, other capabilities may depend
* on the new settings, so we need to check them (again).
*
* @access private
*/
function _reCheckSupportedOptions()
{
$this->supported['transactions'] = $this->options['use_transactions'];
$this->supported['savepoints'] = $this->options['use_transactions'];
if ($this->options['default_table_type']) {
switch (strtoupper($this->options['default_table_type'])) {
case 'BLACKHOLE':
case 'MEMORY':
case 'ARCHIVE':
case 'CSV':
case 'HEAP':
case 'ISAM':
case 'MERGE':
case 'MRG_ISAM':
case 'ISAM':
case 'MRG_MYISAM':
case 'MYISAM':
$this->supported['savepoints'] = false;
$this->supported['transactions'] = false;
$this->warnings[] = $this->options['default_table_type'] .
' is not a supported default table type';
break;
}
}
}
// }}}
// {{{ function setOption($option, $value)
/**
* set the option for the db class
*
* @param string option name
* @param mixed value for the option
*
* @return mixed MDB2_OK or MDB2 Error Object
*
* @access public
*/
function setOption($option, $value)
{
$res = parent::setOption($option, $value);
$this->_reCheckSupportedOptions();
}
// }}}
// {{{ errorInfo()
/**
* This method is used to collect information about an error
*
* @param integer $error
* @return array
* @access public
*/
function errorInfo($error = null)
{
if ($this->connection) {
$native_code = @mysql_errno($this->connection);
$native_msg = @mysql_error($this->connection);
} else {
$native_code = @mysql_errno();
$native_msg = @mysql_error();
}
if (is_null($error)) {
static $ecode_map;
if (empty($ecode_map)) {
$ecode_map = array(
1000 => MDB2_ERROR_INVALID, //hashchk
1001 => MDB2_ERROR_INVALID, //isamchk
1004 => MDB2_ERROR_CANNOT_CREATE,
1005 => MDB2_ERROR_CANNOT_CREATE,
1006 => MDB2_ERROR_CANNOT_CREATE,
1007 => MDB2_ERROR_ALREADY_EXISTS,
1008 => MDB2_ERROR_CANNOT_DROP,
1009 => MDB2_ERROR_CANNOT_DROP,
1010 => MDB2_ERROR_CANNOT_DROP,
1011 => MDB2_ERROR_CANNOT_DELETE,
1022 => MDB2_ERROR_ALREADY_EXISTS,
1029 => MDB2_ERROR_NOT_FOUND,
1032 => MDB2_ERROR_NOT_FOUND,
1044 => MDB2_ERROR_ACCESS_VIOLATION,
1045 => MDB2_ERROR_ACCESS_VIOLATION,
1046 => MDB2_ERROR_NODBSELECTED,
1048 => MDB2_ERROR_CONSTRAINT,
1049 => MDB2_ERROR_NOSUCHDB,
1050 => MDB2_ERROR_ALREADY_EXISTS,
1051 => MDB2_ERROR_NOSUCHTABLE,
1054 => MDB2_ERROR_NOSUCHFIELD,
1060 => MDB2_ERROR_ALREADY_EXISTS,
1061 => MDB2_ERROR_ALREADY_EXISTS,
1062 => MDB2_ERROR_ALREADY_EXISTS,
1064 => MDB2_ERROR_SYNTAX,
1067 => MDB2_ERROR_INVALID,
1072 => MDB2_ERROR_NOT_FOUND,
1086 => MDB2_ERROR_ALREADY_EXISTS,
1091 => MDB2_ERROR_NOT_FOUND,
1100 => MDB2_ERROR_NOT_LOCKED,
1109 => MDB2_ERROR_NOT_FOUND,
1125 => MDB2_ERROR_ALREADY_EXISTS,
1136 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
1138 => MDB2_ERROR_INVALID,
1142 => MDB2_ERROR_ACCESS_VIOLATION,
1143 => MDB2_ERROR_ACCESS_VIOLATION,
1146 => MDB2_ERROR_NOSUCHTABLE,
1149 => MDB2_ERROR_SYNTAX,
1169 => MDB2_ERROR_CONSTRAINT,
1176 => MDB2_ERROR_NOT_FOUND,
1177 => MDB2_ERROR_NOSUCHTABLE,
1213 => MDB2_ERROR_DEADLOCK,
1216 => MDB2_ERROR_CONSTRAINT,
1217 => MDB2_ERROR_CONSTRAINT,
1227 => MDB2_ERROR_ACCESS_VIOLATION,
1235 => MDB2_ERROR_CANNOT_CREATE,
1299 => MDB2_ERROR_INVALID_DATE,
1300 => MDB2_ERROR_INVALID,
1304 => MDB2_ERROR_ALREADY_EXISTS,
1305 => MDB2_ERROR_NOT_FOUND,
1306 => MDB2_ERROR_CANNOT_DROP,
1307 => MDB2_ERROR_CANNOT_CREATE,
1334 => MDB2_ERROR_CANNOT_ALTER,
1339 => MDB2_ERROR_NOT_FOUND,
1356 => MDB2_ERROR_INVALID,
1359 => MDB2_ERROR_ALREADY_EXISTS,
1360 => MDB2_ERROR_NOT_FOUND,
1363 => MDB2_ERROR_NOT_FOUND,
1365 => MDB2_ERROR_DIVZERO,
1451 => MDB2_ERROR_CONSTRAINT,
1452 => MDB2_ERROR_CONSTRAINT,
1542 => MDB2_ERROR_CANNOT_DROP,
1546 => MDB2_ERROR_CONSTRAINT,
1582 => MDB2_ERROR_CONSTRAINT,
2003 => MDB2_ERROR_CONNECT_FAILED,
2019 => MDB2_ERROR_INVALID,
);
}
if ($this->options['portability'] & MDB2_PORTABILITY_ERRORS) {
$ecode_map[1022] = MDB2_ERROR_CONSTRAINT;
$ecode_map[1048] = MDB2_ERROR_CONSTRAINT_NOT_NULL;
$ecode_map[1062] = MDB2_ERROR_CONSTRAINT;
} else {
// Doing this in case mode changes during runtime.
$ecode_map[1022] = MDB2_ERROR_ALREADY_EXISTS;
$ecode_map[1048] = MDB2_ERROR_CONSTRAINT;
$ecode_map[1062] = MDB2_ERROR_ALREADY_EXISTS;
}
if (isset($ecode_map[$native_code])) {
$error = $ecode_map[$native_code];
}
}
return array($error, $native_code, $native_msg);
}
// }}}
// {{{ escape()
/**
* Quotes a string so it can be safely used in a query. It will quote
* the text so it can safely be used within a query.
*
* @param string the input string to quote
* @param bool escape wildcards
*
* @return string quoted string
*
* @access public
*/
function escape($text, $escape_wildcards = false)
{
if ($escape_wildcards) {
$text = $this->escapePattern($text);
}
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$text = @mysql_real_escape_string($text, $connection);
return $text;
}
// }}}
// {{{ beginTransaction()
/**
* Start a transaction or set a savepoint.
*
* @param string name of a savepoint to set
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function beginTransaction($savepoint = null)
{
$this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
$this->_getServerCapabilities();
if (!is_null($savepoint)) {
if (!$this->supports('savepoints')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'savepoint cannot be released when changes are auto committed', __FUNCTION__);
}
$query = 'SAVEPOINT '.$savepoint;
return $this->_doQuery($query, true);
} elseif ($this->in_transaction) {
return MDB2_OK; //nothing to do
}
if (!$this->destructor_registered && $this->opened_persistent) {
$this->destructor_registered = true;
register_shutdown_function('MDB2_closeOpenTransactions');
}
$query = $this->start_transaction ? 'START TRANSACTION' : 'SET AUTOCOMMIT = 0';
$result =& $this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
$this->in_transaction = true;
return MDB2_OK;
}
// }}}
// {{{ commit()
/**
* Commit the database changes done during a transaction that is in
* progress or release a savepoint. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after committing the pending changes.
*
* @param string name of a savepoint to release
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function commit($savepoint = null)
{
$this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
}
if (!is_null($savepoint)) {
if (!$this->supports('savepoints')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
$server_info = $this->getServerVersion();
if (version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '5.0.3', '<')) {
return MDB2_OK;
}
$query = 'RELEASE SAVEPOINT '.$savepoint;
return $this->_doQuery($query, true);
}
if (!$this->supports('transactions')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'transactions are not supported', __FUNCTION__);
}
$result =& $this->_doQuery('COMMIT', true);
if (PEAR::isError($result)) {
return $result;
}
if (!$this->start_transaction) {
$query = 'SET AUTOCOMMIT = 1';
$result =& $this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{ rollback()
/**
* Cancel any database changes done during a transaction or since a specific
* savepoint that is in progress. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after canceling the pending changes.
*
* @param string name of a savepoint to rollback to
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function rollback($savepoint = null)
{
$this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'rollback cannot be done changes are auto committed', __FUNCTION__);
}
if (!is_null($savepoint)) {
if (!$this->supports('savepoints')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
$query = 'ROLLBACK TO SAVEPOINT '.$savepoint;
return $this->_doQuery($query, true);
}
$query = 'ROLLBACK';
$result =& $this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
if (!$this->start_transaction) {
$query = 'SET AUTOCOMMIT = 1';
$result =& $this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{ function setTransactionIsolation()
/**
* Set the transacton isolation level.
*
* @param string standard isolation level
* READ UNCOMMITTED (allows dirty reads)
* READ COMMITTED (prevents dirty reads)
* REPEATABLE READ (prevents nonrepeatable reads)
* SERIALIZABLE (prevents phantom reads)
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
* @since 2.1.1
*/
static function setTransactionIsolation($isolation, $options = array())
{
$this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
if (!$this->supports('transactions')) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'transactions are not supported', __FUNCTION__);
}
switch ($isolation) {
case 'READ UNCOMMITTED':
case 'READ COMMITTED':
case 'REPEATABLE READ':
case 'SERIALIZABLE':
break;
default:
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'isolation level is not supported: '.$isolation, __FUNCTION__);
}
$query = "SET SESSION TRANSACTION ISOLATION LEVEL $isolation";
return $this->_doQuery($query, true);
}
// }}}
// {{{ _doConnect()
/**
* do the grunt work of the connect
*
* @return connection on success or MDB2 Error Object on failure
* @access protected
*/
function _doConnect($username, $password, $persistent = false)
{
if (!PEAR::loadExtension($this->phptype)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
}
$params = array();
if ($this->dsn['protocol'] && $this->dsn['protocol'] == 'unix') {
$params[0] = ':' . $this->dsn['socket'];
} else {
$params[0] = $this->dsn['hostspec'] ? $this->dsn['hostspec']
: 'localhost';
if ($this->dsn['port']) {
$params[0].= ':' . $this->dsn['port'];
}
}
$params[] = $username ? $username : null;
$params[] = $password ? $password : null;
if (!$persistent) {
if ($this->_isNewLinkSet()) {
$params[] = true;
} else {
$params[] = false;
}
}
if (version_compare(phpversion(), '4.3.0', '>=')) {
$params[] = isset($this->dsn['client_flags'])
? $this->dsn['client_flags'] : null;
}
$connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
$connection = @call_user_func_array($connect_function, $params);
if (!$connection) {
if (($err = @mysql_error()) != '') {
return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
$err, __FUNCTION__);
} else {
return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
'unable to establish a connection', __FUNCTION__);
}
}
if (!empty($this->dsn['charset'])) {
$result = $this->setCharset($this->dsn['charset'], $connection);
if (PEAR::isError($result)) {
$this->disconnect(false);
return $result;
}
}
return $connection;
}
// }}}
// {{{ connect()
/**
* Connect to the database
*
* @return MDB2_OK on success, MDB2 Error Object on failure
* @access public
*/
function connect()
{
if (is_resource($this->connection)) {
//if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
if (MDB2::areEquals($this->connected_dsn, $this->dsn)
&& $this->opened_persistent == $this->options['persistent']
) {
return MDB2_OK;
}
$this->disconnect(false);
}
$connection = $this->_doConnect(
$this->dsn['username'],
$this->dsn['password'],
$this->options['persistent']
);
if (PEAR::isError($connection)) {
return $connection;
}
$this->connection = $connection;
$this->connected_dsn = $this->dsn;
$this->connected_database_name = '';
$this->opened_persistent = $this->options['persistent'];
$this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
if ($this->database_name) {
if ($this->database_name != $this->connected_database_name) {
if (!@mysql_select_db($this->database_name, $connection)) {
$err = $this->raiseError(null, null, null,
'Could not select the database: '.$this->database_name, __FUNCTION__);
return $err;
}
$this->connected_database_name = $this->database_name;
}
}
$this->_getServerCapabilities();
return MDB2_OK;
}
// }}}
// {{{ setCharset()
/**
* Set the charset on the current connection
*
* @param string charset (or array(charset, collation))
* @param resource connection handle
*
* @return true on success, MDB2 Error Object on failure
*/
function setCharset($charset, $connection = null)
{
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
$collation = null;
if (is_array($charset) && 2 == count($charset)) {
$collation = array_pop($charset);
$charset = array_pop($charset);
}
$client_info = mysql_get_client_info();
if (function_exists('mysql_set_charset') && version_compare($client_info, '5.0.6')) {
if (!$result = mysql_set_charset($charset, $connection)) {
$err =& $this->raiseError(null, null, null,
'Could not set client character set', __FUNCTION__);
return $err;
}
return $result;
}
$query = "SET NAMES '".mysql_real_escape_string($charset, $connection)."'";
if (!is_null($collation)) {
$query .= " COLLATE '".mysqli_real_escape_string($connection, $collation)."'";
}
return $this->_doQuery($query, true, $connection);
}
// }}}
// {{{ databaseExists()
/**
* check if given database name is exists?
*
* @param string $name name of the database that should be checked
*
* @return mixed true/false on success, a MDB2 error on failure
* @access public
*/
function databaseExists($name)
{
$connection = $this->_doConnect($this->dsn['username'],
$this->dsn['password'],
$this->options['persistent']);
if (PEAR::isError($connection)) {
return $connection;
}
$result = @mysql_select_db($name, $connection);
@mysql_close($connection);
return $result;
}
// }}}
// {{{ disconnect()
/**
* Log out and disconnect from the database.
*
* @param boolean $force if the disconnect should be forced even if the
* connection is opened persistently
* @return mixed true on success, false if not connected and error
* object on error
* @access public
*/
function disconnect($force = true)
{
if (is_resource($this->connection)) {
if ($this->in_transaction) {
$dsn = $this->dsn;
$database_name = $this->database_name;
$persistent = $this->options['persistent'];
$this->dsn = $this->connected_dsn;
$this->database_name = $this->connected_database_name;
$this->options['persistent'] = $this->opened_persistent;
$this->rollback();
$this->dsn = $dsn;
$this->database_name = $database_name;
$this->options['persistent'] = $persistent;
}
if (!$this->opened_persistent || $force) {
$ok = @mysql_close($this->connection);
if (!$ok) {
return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED,
null, null, null, __FUNCTION__);
}
}
} else {
return false;
}
return parent::disconnect($force);
}
// }}}
// {{{ standaloneQuery()
/**
* execute a query as DBA
*
* @param string $query the SQL query
* @param mixed $types array that contains the types of the columns in
* the result set
* @param boolean $is_manip if the query is a manipulation query
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function &standaloneQuery($query, $types = null, $is_manip = false)
{
$user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username'];
$pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password'];
$connection = $this->_doConnect($user, $pass, $this->options['persistent']);
if (PEAR::isError($connection)) {
return $connection;
}
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
$result =& $this->_doQuery($query, $is_manip, $connection, $this->database_name);
if (!PEAR::isError($result)) {
$result = $this->_affectedRows($connection, $result);
}
@mysql_close($connection);
return $result;
}
// }}}
// {{{ _doQuery()
/**
* Execute a query
* @param string $query query
* @param boolean $is_manip if the query is a manipulation query
* @param resource $connection
* @param string $database_name
* @return result or error object
* @access protected
*/
function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
{
$this->last_query = $query;
$result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (PEAR::isError($result)) {
return $result;
}
$query = $result;
}
if ($this->options['disable_query']) {
$result = $is_manip ? 0 : null;
return $result;
}
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
if (is_null($database_name)) {
$database_name = $this->database_name;
}
if ($database_name) {
if ($database_name != $this->connected_database_name) {
if (!@mysql_select_db($database_name, $connection)) {
$err = $this->raiseError(null, null, null,
'Could not select the database: '.$database_name, __FUNCTION__);
return $err;
}
$this->connected_database_name = $database_name;
}
}
$function = $this->options['result_buffering']
? 'mysql_query' : 'mysql_unbuffered_query';
$result = @$function($query, $connection);
if (!$result) {
$err =& $this->raiseError(null, null, null,
'Could not execute statement', __FUNCTION__);
return $err;
}
$this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
return $result;
}
// }}}
// {{{ _affectedRows()
/**
* Returns the number of rows affected
*
* @param resource $result
* @param resource $connection
* @return mixed MDB2 Error Object or the number of rows affected
* @access private
*/
function _affectedRows($connection, $result = null)
{
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
return @mysql_affected_rows($connection);
}
// }}}
// {{{ _modifyQuery()
/**
* Changes a query string for various DBMS specific reasons
*
* @param string $query query to modify
* @param boolean $is_manip if it is a DML query
* @param integer $limit limit the number of rows
* @param integer $offset start reading from given offset
* @return string modified query
* @access protected
*/
function _modifyQuery($query, $is_manip, $limit, $offset)
{
if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) {
// "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);
}
}
if ($limit > 0
&& !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query)
) {
$query = rtrim($query);
if (substr($query, -1) == ';') {
$query = substr($query, 0, -1);
}
// LIMIT doesn't always come last in the query
// @see http://dev.mysql.com/doc/refman/5.0/en/select.html
$after = '';
if (preg_match('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', $query, $matches)) {
$after = $matches[0];
$query = preg_replace('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', '', $query);
} elseif (preg_match('/(\s+FOR\s+UPDATE\s*)$/i', $query, $matches)) {
$after = $matches[0];
$query = preg_replace('/(\s+FOR\s+UPDATE\s*)$/im', '', $query);
} elseif (preg_match('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', $query, $matches)) {
$after = $matches[0];
$query = preg_replace('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', '', $query);
}
if ($is_manip) {
return $query . " LIMIT $limit" . $after;
} else {
return $query . " LIMIT $offset, $limit" . $after;
}
}
return $query;
}
// }}}
// {{{ getServerVersion()
/**
* return version information about the server
*
* @param bool $native determines if the raw version string should be returned
* @return mixed array/string with version information or MDB2 error object
* @access public
*/
function getServerVersion($native = false)
{
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
if ($this->connected_server_info) {
$server_info = $this->connected_server_info;
} else {
$server_info = @mysql_get_server_info($connection);
}
if (!$server_info) {
return $this->raiseError(null, null, null,
'Could not get server information', __FUNCTION__);
}
// cache server_info
$this->connected_server_info = $server_info;
if (!$native) {
$tmp = explode('.', $server_info, 3);
if (isset($tmp[2]) && strpos($tmp[2], '-')) {
$tmp2 = explode('-', @$tmp[2], 2);
} else {
$tmp2[0] = isset($tmp[2]) ? $tmp[2] : null;
$tmp2[1] = null;
}
$server_info = array(
'major' => isset($tmp[0]) ? $tmp[0] : null,
'minor' => isset($tmp[1]) ? $tmp[1] : null,
'patch' => $tmp2[0],
'extra' => $tmp2[1],
'native' => $server_info,
);
}
return $server_info;
}
// }}}
// {{{ _getServerCapabilities()
/**
* Fetch some information about the server capabilities
* (transactions, subselects, prepared statements, etc).
*
* @access private
*/
function _getServerCapabilities()
{
if (!$this->server_capabilities_checked) {
$this->server_capabilities_checked = true;
//set defaults
$this->supported['sub_selects'] = 'emulated';
$this->supported['prepared_statements'] = 'emulated';
$this->supported['triggers'] = false;
$this->start_transaction = false;
$this->varchar_max_length = 255;
$server_info = $this->getServerVersion();
if (is_array($server_info)) {
$server_version = $server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'];
if (!version_compare($server_version, '4.1.0', '<')) {
$this->supported['sub_selects'] = true;
$this->supported['prepared_statements'] = true;
}
// SAVEPOINTs were introduced in MySQL 4.0.14 and 4.1.1 (InnoDB)
if (version_compare($server_version, '4.1.0', '>=')) {
if (version_compare($server_version, '4.1.1', '<')) {
$this->supported['savepoints'] = false;
}
} elseif (version_compare($server_version, '4.0.14', '<')) {
$this->supported['savepoints'] = false;
}
if (!version_compare($server_version, '4.0.11', '<')) {
$this->start_transaction = true;
}
if (!version_compare($server_version, '5.0.3', '<')) {
$this->varchar_max_length = 65532;
}
if (!version_compare($server_version, '5.0.2', '<')) {
$this->supported['triggers'] = true;
}
}
}
}
// }}}
// {{{ function _skipUserDefinedVariable($query, $position)
/**
* Utility method, used by prepare() to avoid misinterpreting MySQL user
* defined variables (SELECT @x:=5) for placeholders.
* Check if the placeholder is a false positive, i.e. if it is an user defined
* variable instead. If so, skip it and advance the position, otherwise
* return the current position, which is valid
*
* @param string $query
* @param integer $position current string cursor position
* @return integer $new_position
* @access protected
*/
function _skipUserDefinedVariable($query, $position)
{
$found = strpos(strrev(substr($query, 0, $position)), '@');
if ($found === false) {
return $position;
}
$pos = strlen($query) - strlen(substr($query, $position)) - $found - 1;
$substring = substr($query, $pos, $position - $pos + 2);
if (preg_match('/^@\w+\s*:=$/', $substring)) {
return $position + 1; //found an user defined variable: skip it
}
return $position;
}
// }}}
// {{{ prepare()
/**
* Prepares a query for multiple execution with execute().
* With some database backends, this is emulated.
* prepare() requires a generic query as string like
* 'INSERT INTO numbers VALUES(?,?)' or
* 'INSERT INTO numbers VALUES(:foo,:bar)'.
* The ? and :name and are placeholders which can be set using
* bindParam() and the query can be sent off using the execute() method.
* The allowed format for :name can be set with the 'bindname_format' option.
*
* @param string $query the query to prepare
* @param mixed $types array that contains the types of the placeholders
* @param mixed $result_types array that contains the types of the columns in
* the result set or MDB2_PREPARE_RESULT, if set to
* MDB2_PREPARE_MANIP the query is handled as a manipulation query
* @param mixed $lobs key (field) value (parameter) pair for all lob placeholders
* @return mixed resource handle for the prepared query on success, a MDB2
* error on failure
* @access public
* @see bindParam, execute
*/
function &prepare($query, $types = null, $result_types = null, $lobs = array())
{
if ($this->options['emulate_prepared']
|| $this->supported['prepared_statements'] !== true
) {
$obj =& parent::prepare($query, $types, $result_types, $lobs);
return $obj;
}
$is_manip = ($result_types === MDB2_PREPARE_MANIP);
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
$result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (PEAR::isError($result)) {
return $result;
}
$query = $result;
}
$placeholder_type_guess = $placeholder_type = null;
$question = '?';
$colon = ':';
$positions = array();
$position = 0;
while ($position < strlen($query)) {
$q_position = strpos($query, $question, $position);
$c_position = strpos($query, $colon, $position);
if ($q_position && $c_position) {
$p_position = min($q_position, $c_position);
} elseif ($q_position) {
$p_position = $q_position;
} elseif ($c_position) {
$p_position = $c_position;
} else {
break;
}
if (is_null($placeholder_type)) {
$placeholder_type_guess = $query[$p_position];
}
$new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
if (PEAR::isError($new_pos)) {
return $new_pos;
}
if ($new_pos != $position) {
$position = $new_pos;
continue; //evaluate again starting from the new position
}
//make sure this is not part of an user defined variable
$new_pos = $this->_skipUserDefinedVariable($query, $position);
if ($new_pos != $position) {
$position = $new_pos;
continue; //evaluate again starting from the new position
}
if ($query[$position] == $placeholder_type_guess) {
if (is_null($placeholder_type)) {
$placeholder_type = $query[$p_position];
$question = $colon = $placeholder_type;
}
if ($placeholder_type == ':') {
$regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s';
$parameter = preg_replace($regexp, '\\1', $query);
if ($parameter === '') {
$err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'named parameter name must match "bindname_format" option', __FUNCTION__);
return $err;
}
$positions[$p_position] = $parameter;
$query = substr_replace($query, '?', $position, strlen($parameter)+1);
} else {
$positions[$p_position] = count($positions);
}
$position = $p_position + 1;
} else {
$position = $p_position;
}
}
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
static $prep_statement_counter = 1;
$statement_name = sprintf($this->options['statement_format'], $this->phptype, $prep_statement_counter++ . sha1(microtime() + mt_rand()));
$statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']);
$query = "PREPARE $statement_name FROM ".$this->quote($query, 'text');
$statement =& $this->_doQuery($query, true, $connection);
if (PEAR::isError($statement)) {
return $statement;
}
$class_name = 'MDB2_Statement_'.$this->phptype;
$obj = new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
$this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
return $obj;
}
// }}}
// {{{ replace()
/**
* Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
* query, except that if there is already a row in the table with the same
* key field values, the old row is deleted before the new row is inserted.
*
* The REPLACE type of query does not make part of the SQL standards. Since
* practically only MySQL implements it natively, this type of query is
* emulated through this method for other DBMS using standard types of
* queries inside a transaction to assure the atomicity of the operation.
*
* @access public
*
* @param string $table name of the table on which the REPLACE query will
* be executed.
* @param array $fields associative array that describes the fields and the
* values that will be inserted or updated in the specified table. The
* indexes of the array are the names of all the fields of the table. The
* values of the array are also associative arrays that describe the
* values and other properties of the table fields.
*
* Here follows a list of field properties that need to be specified:
*
* value:
* Value to be assigned to the specified field. This value may be
* of specified in database independent type format as this
* function can perform the necessary datatype conversions.
*
* Default:
* this property is required unless the Null property
* is set to 1.
*
* type
* Name of the type of the field. Currently, all types Metabase
* are supported except for clob and blob.
*
* Default: no type conversion
*
* null
* Boolean property that indicates that the value for this field
* should be set to null.
*
* The default value for fields missing in INSERT queries may be
* specified the definition of a table. Often, the default value
* is already null, but since the REPLACE may be emulated using
* an UPDATE query, make sure that all fields of the table are
* listed in this function argument array.
*
* Default: 0
*
* key
* Boolean property that indicates that this field should be
* handled as a primary key or at least as part of the compound
* unique index of the table that will determine the row that will
* updated if it exists or inserted a new row otherwise.
*
* This function will fail if no key field is specified or if the
* value of a key field is set to null because fields that are
* part of unique index they may not be null.
*
* Default: 0
*
* @see http://dev.mysql.com/doc/refman/5.0/en/replace.html
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function replace($table, $fields)
{
$count = count($fields);
$query = $values = '';
$keys = $colnum = 0;
for (reset($fields); $colnum < $count; next($fields), $colnum++) {
$name = key($fields);
if ($colnum > 0) {
$query .= ',';
$values.= ',';
}
$query.= $this->quoteIdentifier($name, true);
if (isset($fields[$name]['null']) && $fields[$name]['null']) {
$value = 'NULL';
} else {
$type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
$value = $this->quote($fields[$name]['value'], $type);
if (PEAR::isError($value)) {
return $value;
}
}
$values.= $value;
if (isset($fields[$name]['key']) && $fields[$name]['key']) {
if ($value === 'NULL') {
return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
'key value '.$name.' may not be NULL', __FUNCTION__);
}
$keys++;
}
}
if ($keys == 0) {
return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
'not specified which fields are keys', __FUNCTION__);
}
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$table = $this->quoteIdentifier($table, true);
$query = "REPLACE INTO $table ($query) VALUES ($values)";
$result =& $this->_doQuery($query, true, $connection);
if (PEAR::isError($result)) {
return $result;
}
return $this->_affectedRows($connection, $result);
}
// }}}
// {{{ nextID()
/**
* Returns the next free id of a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true the sequence is
* automatic created, if it
* not exists
*
* @return mixed MDB2 Error Object or id
* @access public
*/
function nextID($seq_name, $ondemand = true)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
$seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
$query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)";
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$this->expectError(MDB2_ERROR_NOSUCHTABLE);
$result =& $this->_doQuery($query, true);
$this->popExpect();
$this->popErrorHandling();
if (PEAR::isError($result)) {
if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
$this->loadModule('Manager', null, true);
$result = $this->manager->createSequence($seq_name);
if (PEAR::isError($result)) {
return $this->raiseError($result, null, null,
'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
} else {
return $this->nextID($seq_name, false);
}
}
return $result;
}
$value = $this->lastInsertID();
if (is_numeric($value)) {
$query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
$result =& $this->_doQuery($query, true);
if (PEAR::isError($result)) {
$this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
}
}
return $value;
}
// }}}
// {{{ lastInsertID()
/**
* Returns the autoincrement ID if supported or $id or fetches the current
* ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
*
* @param string $table name of the table into which a new row was inserted
* @param string $field name of the field into which a new row was inserted
* @return mixed MDB2 Error Object or id
* @access public
*/
function lastInsertID($table = null, $field = null)
{
// not using mysql_insert_id() due to http://pear.php.net/bugs/bug.php?id=8051
return $this->queryOne('SELECT LAST_INSERT_ID()', 'integer');
}
// }}}
// {{{ currID()
/**
* Returns the current id of a sequence
*
* @param string $seq_name name of the sequence
* @return mixed MDB2 Error Object or id
* @access public
*/
function currID($seq_name)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
$seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
$query = "SELECT MAX($seqcol_name) FROM $sequence_name";
return $this->queryOne($query, 'integer');
}
}
/**
* MDB2 MySQL result driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Result_mysql extends MDB2_Result_Common
{
// }}}
// {{{ fetchRow()
/**
* Fetch a row and insert the data into an existing array.
*
* @param int $fetchmode how the array data should be indexed
* @param int $rownum number of the row where the data can be found
* @return int data array on success, a MDB2 error on failure
* @access public
*/
function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
{
if (!is_null($rownum)) {
$seek = $this->seek($rownum);
if (PEAR::isError($seek)) {
return $seek;
}
}
if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
$fetchmode = $this->db->fetchmode;
}
if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
$row = @mysql_fetch_assoc($this->result);
if (is_array($row)
&& $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
) {
$row = array_change_key_case($row, $this->db->options['field_case']);
}
} else {
$row = @mysql_fetch_row($this->result);
}
if (!$row) {
if ($this->result === false) {
$err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
return $err;
}
$null = null;
return $null;
}
$mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
$rtrim = false;
if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
if (empty($this->types)) {
$mode += MDB2_PORTABILITY_RTRIM;
} else {
$rtrim = true;
}
}
if ($mode) {
$this->db->_fixResultArrayValues($row, $mode);
}
if (!empty($this->types)) {
$row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
}
if (!empty($this->values)) {
$this->_assignBindColumns($row);
}
if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
$object_class = $this->db->options['fetch_class'];
if ($object_class == 'stdClass') {
$row = (object) $row;
} else {
$row = new $object_class($row);
}
}
++$this->rownum;
return $row;
}
// }}}
// {{{ _getColumnNames()
/**
* Retrieve the names of columns returned by the DBMS in a query result.
*
* @return mixed Array variable that holds the names of columns as keys
* or an MDB2 error on failure.
* Some DBMS may not return any columns when the result set
* does not contain any rows.
* @access private
*/
function _getColumnNames()
{
$columns = array();
$numcols = $this->numCols();
if (PEAR::isError($numcols)) {
return $numcols;
}
for ($column = 0; $column < $numcols; $column++) {
$column_name = @mysql_field_name($this->result, $column);
$columns[$column_name] = $column;
}
if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$columns = array_change_key_case($columns, $this->db->options['field_case']);
}
return $columns;
}
// }}}
// {{{ numCols()
/**
* Count the number of columns returned by the DBMS in a query result.
*
* @return mixed integer value with the number of columns, a MDB2 error
* on failure
* @access public
*/
function numCols()
{
$cols = @mysql_num_fields($this->result);
if (is_null($cols)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return count($this->types);
}
return $this->db->raiseError(null, null, null,
'Could not get column count', __FUNCTION__);
}
return $cols;
}
// }}}
// {{{ free()
/**
* Free the internal resources associated with result.
*
* @return boolean true on success, false if result is invalid
* @access public
*/
function free()
{
if (is_resource($this->result) && $this->db->connection) {
$free = @mysql_free_result($this->result);
if ($free === false) {
return $this->db->raiseError(null, null, null,
'Could not free result', __FUNCTION__);
}
}
$this->result = false;
return MDB2_OK;
}
}
/**
* MDB2 MySQL buffered result driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_BufferedResult_mysql extends MDB2_Result_mysql
{
// }}}
// {{{ seek()
/**
* Seek to a specific row in a result set
*
* @param int $rownum number of the row where the data can be found
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function seek($rownum = 0)
{
if ($this->rownum != ($rownum - 1) && !@mysql_data_seek($this->result, $rownum)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return MDB2_OK;
}
return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
}
$this->rownum = $rownum - 1;
return MDB2_OK;
}
// }}}
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return mixed true or false on sucess, a MDB2 error on failure
* @access public
*/
function valid()
{
$numrows = $this->numRows();
if (PEAR::isError($numrows)) {
return $numrows;
}
return $this->rownum < ($numrows - 1);
}
// }}}
// {{{ numRows()
/**
* Returns the number of rows in a result object
*
* @return mixed MDB2 Error Object or the number of rows
* @access public
*/
function numRows()
{
$rows = @mysql_num_rows($this->result);
if (false === $rows) {
if (false === $this->result) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return 0;
}
return $this->db->raiseError(null, null, null,
'Could not get row count', __FUNCTION__);
}
return $rows;
}
}
/**
* MDB2 MySQL statement driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Statement_mysql extends MDB2_Statement_Common
{
// {{{ _execute()
/**
* Execute a prepared query statement helper method.
*
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
*
* @return mixed MDB2_Result or integer (affected rows) on success,
* a MDB2 error on failure
* @access private
*/
function &_execute($result_class = true, $result_wrap_class = false)
{
if (is_null($this->statement)) {
$result =& parent::_execute($result_class, $result_wrap_class);
return $result;
}
$this->db->last_query = $this->query;
$this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values));
if ($this->db->getOption('disable_query')) {
$result = $this->is_manip ? 0 : null;
return $result;
}
$connection = $this->db->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$query = 'EXECUTE '.$this->statement;
if (!empty($this->positions)) {
$parameters = array();
foreach ($this->positions as $parameter) {
if (!array_key_exists($parameter, $this->values)) {
return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
}
$value = $this->values[$parameter];
$type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
if (is_resource($value) || $type == 'clob' || $type == 'blob' && $this->db->options['lob_allow_url_include']) {
if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
if ($match[1] == 'file://') {
$value = $match[2];
}
$value = @fopen($value, 'r');
$close = true;
}
if (is_resource($value)) {
$data = '';
while (!@feof($value)) {
$data.= @fread($value, $this->db->options['lob_buffer_length']);
}
if ($close) {
@fclose($value);
}
$value = $data;
}
}
$quoted = $this->db->quote($value, $type);
if (PEAR::isError($quoted)) {
return $quoted;
}
$param_query = 'SET @'.$parameter.' = '.$quoted;
$result = $this->db->_doQuery($param_query, true, $connection);
if (PEAR::isError($result)) {
return $result;
}
}
$query.= ' USING @'.implode(', @', array_values($this->positions));
}
$result = $this->db->_doQuery($query, $this->is_manip, $connection);
if (PEAR::isError($result)) {
return $result;
}
if ($this->is_manip) {
$affected_rows = $this->db->_affectedRows($connection, $result);
return $affected_rows;
}
$result =& $this->db->_wrapResult($result, $this->result_types,
$result_class, $result_wrap_class, $this->limit, $this->offset);
$this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
return $result;
}
// }}}
// {{{ free()
/**
* Release resources allocated for the specified prepared query.
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function free()
{
if (is_null($this->positions)) {
return $this->db->raiseError(MDB2_ERROR, null, null,
'Prepared statement has already been freed', __FUNCTION__);
}
$result = MDB2_OK;
if (!is_null($this->statement)) {
$connection = $this->db->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$query = 'DEALLOCATE PREPARE '.$this->statement;
$result = $this->db->_doQuery($query, true, $connection);
}
parent::free();
return $result;
}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: Common.php,v 1.2 2007/09/09 13:47:36 quipo Exp $
//
/**
* Base class for the natuve modules that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Native');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Native_Common extends MDB2_Module_Common
{
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: mysql.php,v 1.9 2006/06/18 21:59:05 lsmith Exp $
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 MySQL driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Native_mysql extends MDB2_Driver_Native_Common
{
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id: pgsql.php,v 1.12 2006/07/15 13:07:15 lsmith Exp $
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 PostGreSQL driver for the native module
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Driver_Native_pgsql extends MDB2_Driver_Native_Common
{
// }}}
// {{{ deleteOID()
/**
* delete an OID
*
* @param integer $OID
* @return mixed MDB2_OK on success or MDB2 Error Object on failure
* @access public
*/
function deleteOID($OID)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$connection = $db->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
if (!@pg_lo_unlink($connection, $OID)) {
return $db->raiseError(null, null, null,
'Unable to unlink OID: '.$OID, __FUNCTION__);
}
return MDB2_OK;
}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: sqlite.php,v 1.9 2006/06/18 21:59:05 lsmith Exp $
//
require_once 'MDB2/Driver/Native/Common.php';
/**
* MDB2 SQLite driver for the native module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Native_sqlite extends MDB2_Driver_Native_Common
{
}
?>
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Paul Cooper <pgc@ucecom.com> |
// +----------------------------------------------------------------------+
//
// $Id: pgsql.php,v 1.203 2008/11/29 14:04:46 afz Exp $
/**
* MDB2 PostGreSQL driver
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Driver_pgsql extends MDB2_Driver_Common
{
// {{{ properties
var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => '\\');
var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
// }}}
// {{{ constructor
/**
* Constructor
*/
function __construct()
{
parent::__construct();
$this->phptype = 'pgsql';
$this->dbsyntax = 'pgsql';
$this->supported['sequences'] = true;
$this->supported['indexes'] = true;
$this->supported['affected_rows'] = true;
$this->supported['summary_functions'] = true;
$this->supported['order_by_text'] = true;
$this->supported['transactions'] = true;
$this->supported['savepoints'] = true;
$this->supported['current_id'] = true;
$this->supported['limit_queries'] = true;
$this->supported['LOBs'] = true;
$this->supported['replace'] = 'emulated';
$this->supported['sub_selects'] = true;
$this->supported['triggers'] = true;
$this->supported['auto_increment'] = 'emulated';
$this->supported['primary_key'] = true;
$this->supported['result_introspection'] = true;
$this->supported['prepared_statements'] = true;
$this->supported['identifier_quoting'] = true;
$this->supported['pattern_escaping'] = true;
$this->supported['new_link'] = true;
$this->options['DBA_username'] = false;
$this->options['DBA_password'] = false;
$this->options['multi_query'] = false;
$this->options['disable_smart_seqname'] = true;
$this->options['max_identifiers_length'] = 63;
}
// }}}
// {{{ errorInfo()
/**
* This method is used to collect information about an error
*
* @param integer $error
* @return array
* @access public
*/
function errorInfo($error = null)
{
// Fall back to MDB2_ERROR if there was no mapping.
$error_code = MDB2_ERROR;
$native_msg = '';
if (is_resource($error)) {
$native_msg = @pg_result_error($error);
} elseif ($this->connection) {
$native_msg = @pg_last_error($this->connection);
if (!$native_msg && @pg_connection_status($this->connection) === PGSQL_CONNECTION_BAD) {
$native_msg = 'Database connection has been lost.';
$error_code = MDB2_ERROR_CONNECT_FAILED;
}
} else {
$native_msg = @pg_last_error();
}
static $error_regexps;
if (empty($error_regexps)) {
$error_regexps = array(
'/column .* (of relation .*)?does not exist/i'
=> MDB2_ERROR_NOSUCHFIELD,
'/(relation|sequence|table).*does not exist|class .* not found/i'
=> MDB2_ERROR_NOSUCHTABLE,
'/database .* does not exist/'
=> MDB2_ERROR_NOT_FOUND,
'/constraint .* does not exist/'
=> MDB2_ERROR_NOT_FOUND,
'/index .* does not exist/'
=> MDB2_ERROR_NOT_FOUND,
'/database .* already exists/i'
=> MDB2_ERROR_ALREADY_EXISTS,
'/relation .* already exists/i'
=> MDB2_ERROR_ALREADY_EXISTS,
'/(divide|division) by zero$/i'
=> MDB2_ERROR_DIVZERO,
'/pg_atoi: error in .*: can\'t parse /i'
=> MDB2_ERROR_INVALID_NUMBER,
'/invalid input syntax for( type)? (integer|numeric)/i'
=> MDB2_ERROR_INVALID_NUMBER,
'/value .* is out of range for type \w*int/i'
=> MDB2_ERROR_INVALID_NUMBER,
'/integer out of range/i'
=> MDB2_ERROR_INVALID_NUMBER,
'/value too long for type character/i'
=> MDB2_ERROR_INVALID,
'/attribute .* not found|relation .* does not have attribute/i'
=> MDB2_ERROR_NOSUCHFIELD,
'/column .* specified in USING clause does not exist in (left|right) table/i'
=> MDB2_ERROR_NOSUCHFIELD,
'/parser: parse error at or near/i'
=> MDB2_ERROR_SYNTAX,
'/syntax error at/'
=> MDB2_ERROR_SYNTAX,
'/column reference .* is ambiguous/i'
=> MDB2_ERROR_SYNTAX,
'/permission denied/'
=> MDB2_ERROR_ACCESS_VIOLATION,
'/violates not-null constraint/'
=> MDB2_ERROR_CONSTRAINT_NOT_NULL,
'/violates [\w ]+ constraint/'
=> MDB2_ERROR_CONSTRAINT,
'/referential integrity violation/'
=> MDB2_ERROR_CONSTRAINT,
'/more expressions than target columns/i'
=> MDB2_ERROR_VALUE_COUNT_ON_ROW,
);
}
if (is_numeric($error) && $error < 0) {
$error_code = $error;
} else {
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $native_msg)) {
$error_code = $code;
break;
}
}
}
return array($error_code, null, $native_msg);
}
// }}}
// {{{ escape()
/**
* Quotes a string so it can be safely used in a query. It will quote
* the text so it can safely be used within a query.
*
* @param string the input string to quote
* @param bool escape wildcards
*
* @return string quoted string
*
* @access public
*/
function escape($text, $escape_wildcards = false)
{
if ($escape_wildcards) {
$text = $this->escapePattern($text);
}
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
if (is_resource($connection) && version_compare(PHP_VERSION, '5.2.0RC5', '>=')) {
$text = @pg_escape_string($connection, $text);
} else {
$text = @pg_escape_string($text);
}
return $text;
}
// }}}
// {{{ beginTransaction()
/**
* Start a transaction or set a savepoint.
*
* @param string name of a savepoint to set
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function beginTransaction($savepoint = null)
{
$this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!is_null($savepoint)) {
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'savepoint cannot be released when changes are auto committed', __FUNCTION__);
}
$query = 'SAVEPOINT '.$savepoint;
return $this->_doQuery($query, true);
} elseif ($this->in_transaction) {
return MDB2_OK; //nothing to do
}
if (!$this->destructor_registered && $this->opened_persistent) {
$this->destructor_registered = true;
register_shutdown_function('MDB2_closeOpenTransactions');
}
$result =& $this->_doQuery('BEGIN', true);
if (PEAR::isError($result)) {
return $result;
}
$this->in_transaction = true;
return MDB2_OK;
}
// }}}
// {{{ commit()
/**
* Commit the database changes done during a transaction that is in
* progress or release a savepoint. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after committing the pending changes.
*
* @param string name of a savepoint to release
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function commit($savepoint = null)
{
$this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
}
if (!is_null($savepoint)) {
$query = 'RELEASE SAVEPOINT '.$savepoint;
return $this->_doQuery($query, true);
}
$result =& $this->_doQuery('COMMIT', true);
if (PEAR::isError($result)) {
return $result;
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{ rollback()
/**
* Cancel any database changes done during a transaction or since a specific
* savepoint that is in progress. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after canceling the pending changes.
*
* @param string name of a savepoint to rollback to
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function rollback($savepoint = null)
{
$this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'rollback cannot be done changes are auto committed', __FUNCTION__);
}
if (!is_null($savepoint)) {
$query = 'ROLLBACK TO SAVEPOINT '.$savepoint;
return $this->_doQuery($query, true);
}
$query = 'ROLLBACK';
$result =& $this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{ function setTransactionIsolation()
/**
* Set the transacton isolation level.
*
* @param string standard isolation level
* READ UNCOMMITTED (allows dirty reads)
* READ COMMITTED (prevents dirty reads)
* REPEATABLE READ (prevents nonrepeatable reads)
* SERIALIZABLE (prevents phantom reads)
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
* @since 2.1.1
*/
static function setTransactionIsolation($isolation, $options = array())
{
$this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
switch ($isolation) {
case 'READ UNCOMMITTED':
case 'READ COMMITTED':
case 'REPEATABLE READ':
case 'SERIALIZABLE':
break;
default:
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'isolation level is not supported: '.$isolation, __FUNCTION__);
}
$query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL $isolation";
return $this->_doQuery($query, true);
}
// }}}
// {{{ _doConnect()
/**
* Do the grunt work of connecting to the database
*
* @return mixed connection resource on success, MDB2 Error Object on failure
* @access protected
*/
function _doConnect($username, $password, $database_name, $persistent = false)
{
if (!PEAR::loadExtension($this->phptype)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
}
if ($database_name == '') {
$database_name = 'template1';
}
$protocol = $this->dsn['protocol'] ? $this->dsn['protocol'] : 'tcp';
$params = array('');
if ($protocol == 'tcp') {
if ($this->dsn['hostspec']) {
$params[0].= 'host=' . $this->dsn['hostspec'];
}
if ($this->dsn['port']) {
$params[0].= ' port=' . $this->dsn['port'];
}
} elseif ($protocol == 'unix') {
// Allow for pg socket in non-standard locations.
if ($this->dsn['socket']) {
$params[0].= 'host=' . $this->dsn['socket'];
}
if ($this->dsn['port']) {
$params[0].= ' port=' . $this->dsn['port'];
}
}
if ($database_name) {
$params[0].= ' dbname=\'' . addslashes($database_name) . '\'';
}
if ($username) {
$params[0].= ' user=\'' . addslashes($username) . '\'';
}
if ($password) {
$params[0].= ' password=\'' . addslashes($password) . '\'';
}
if (!empty($this->dsn['options'])) {
$params[0].= ' options=' . $this->dsn['options'];
}
if (!empty($this->dsn['tty'])) {
$params[0].= ' tty=' . $this->dsn['tty'];
}
if (!empty($this->dsn['connect_timeout'])) {
$params[0].= ' connect_timeout=' . $this->dsn['connect_timeout'];
}
if (!empty($this->dsn['sslmode'])) {
$params[0].= ' sslmode=' . $this->dsn['sslmode'];
}
if (!empty($this->dsn['service'])) {
$params[0].= ' service=' . $this->dsn['service'];
}
if ($this->_isNewLinkSet()) {
if (version_compare(phpversion(), '4.3.0', '>=')) {
$params[] = PGSQL_CONNECT_FORCE_NEW;
}
}
$connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
$connection = @call_user_func_array($connect_function, $params);
if (!$connection) {
return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
'unable to establish a connection', __FUNCTION__);
}
if (empty($this->dsn['disable_iso_date'])) {
if (!@pg_query($connection, "SET SESSION DATESTYLE = 'ISO'")) {
return $this->raiseError(null, null, null,
'Unable to set date style to iso', __FUNCTION__);
}
}
if (!empty($this->dsn['charset'])) {
$result = $this->setCharset($this->dsn['charset'], $connection);
if (PEAR::isError($result)) {
return $result;
}
}
return $connection;
}
// }}}
// {{{ connect()
/**
* Connect to the database
*
* @return true on success, MDB2 Error Object on failure
* @access public
*/
function connect()
{
if (is_resource($this->connection)) {
//if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
if (MDB2::areEquals($this->connected_dsn, $this->dsn)
&& $this->connected_database_name == $this->database_name
&& ($this->opened_persistent == $this->options['persistent'])
) {
return MDB2_OK;
}
$this->disconnect(false);
}
if ($this->database_name) {
$connection = $this->_doConnect($this->dsn['username'],
$this->dsn['password'],
$this->database_name,
$this->options['persistent']);
if (PEAR::isError($connection)) {
return $connection;
}
$this->connection = $connection;
$this->connected_dsn = $this->dsn;
$this->connected_database_name = $this->database_name;
$this->opened_persistent = $this->options['persistent'];
$this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
}
return MDB2_OK;
}
// }}}
// {{{ setCharset()
/**
* Set the charset on the current connection
*
* @param string charset
* @param resource connection handle
*
* @return true on success, MDB2 Error Object on failure
*/
function setCharset($charset, $connection = null)
{
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
if (is_array($charset)) {
$charset = array_shift($charset);
$this->warnings[] = 'postgresql does not support setting client collation';
}
$result = @pg_set_client_encoding($connection, $charset);
if ($result == -1) {
return $this->raiseError(null, null, null,
'Unable to set client charset: '.$charset, __FUNCTION__);
}
return MDB2_OK;
}
// }}}
// {{{ databaseExists()
/**
* check if given database name is exists?
*
* @param string $name name of the database that should be checked
*
* @return mixed true/false on success, a MDB2 error on failure
* @access public
*/
function databaseExists($name)
{
$res = $this->_doConnect($this->dsn['username'],
$this->dsn['password'],
$this->escape($name),
$this->options['persistent']);
if (!PEAR::isError($res)) {
return true;
}
return false;
}
// }}}
// {{{ disconnect()
/**
* Log out and disconnect from the database.
*
* @param boolean $force if the disconnect should be forced even if the
* connection is opened persistently
* @return mixed true on success, false if not connected and error
* object on error
* @access public
*/
function disconnect($force = true)
{
if (is_resource($this->connection)) {
if ($this->in_transaction) {
$dsn = $this->dsn;
$database_name = $this->database_name;
$persistent = $this->options['persistent'];
$this->dsn = $this->connected_dsn;
$this->database_name = $this->connected_database_name;
$this->options['persistent'] = $this->opened_persistent;
$this->rollback();
$this->dsn = $dsn;
$this->database_name = $database_name;
$this->options['persistent'] = $persistent;
}
if (!$this->opened_persistent || $force) {
$ok = @pg_close($this->connection);
if (!$ok) {
return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED,
null, null, null, __FUNCTION__);
}
}
} else {
return false;
}
return parent::disconnect($force);
}
// }}}
// {{{ standaloneQuery()
/**
* execute a query as DBA
*
* @param string $query the SQL query
* @param mixed $types array that contains the types of the columns in
* the result set
* @param boolean $is_manip if the query is a manipulation query
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function &standaloneQuery($query, $types = null, $is_manip = false)
{
$user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username'];
$pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password'];
$connection = $this->_doConnect($user, $pass, $this->database_name, $this->options['persistent']);
if (PEAR::isError($connection)) {
return $connection;
}
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
$result =& $this->_doQuery($query, $is_manip, $connection, $this->database_name);
if (!PEAR::isError($result)) {
if ($is_manip) {
$result = $this->_affectedRows($connection, $result);
} else {
$result =& $this->_wrapResult($result, $types, true, false, $limit, $offset);
}
}
@pg_close($connection);
return $result;
}
// }}}
// {{{ _doQuery()
/**
* Execute a query
* @param string $query query
* @param boolean $is_manip if the query is a manipulation query
* @param resource $connection
* @param string $database_name
* @return result or error object
* @access protected
*/
function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
{
$this->last_query = $query;
$result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (PEAR::isError($result)) {
return $result;
}
$query = $result;
}
if ($this->options['disable_query']) {
$result = $is_manip ? 0 : null;
return $result;
}
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
$function = $this->options['multi_query'] ? 'pg_send_query' : 'pg_query';
$result = @$function($connection, $query);
if (!$result) {
$err =& $this->raiseError(null, null, null,
'Could not execute statement', __FUNCTION__);
return $err;
} elseif ($this->options['multi_query']) {
if (!($result = @pg_get_result($connection))) {
$err =& $this->raiseError(null, null, null,
'Could not get the first result from a multi query', __FUNCTION__);
return $err;
}
}
$this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
return $result;
}
// }}}
// {{{ _affectedRows()
/**
* Returns the number of rows affected
*
* @param resource $result
* @param resource $connection
* @return mixed MDB2 Error Object or the number of rows affected
* @access private
*/
function _affectedRows($connection, $result = null)
{
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
return @pg_affected_rows($result);
}
// }}}
// {{{ _modifyQuery()
/**
* Changes a query string for various DBMS specific reasons
*
* @param string $query query to modify
* @param boolean $is_manip if it is a DML query
* @param integer $limit limit the number of rows
* @param integer $offset start reading from given offset
* @return string modified query
* @access protected
*/
function _modifyQuery($query, $is_manip, $limit, $offset)
{
if ($limit > 0
&& !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query)
) {
$query = rtrim($query);
if (substr($query, -1) == ';') {
$query = substr($query, 0, -1);
}
if ($is_manip) {
$query = $this->_modifyManipQuery($query, $limit);
} else {
$query.= " LIMIT $limit OFFSET $offset";
}
}
return $query;
}
// }}}
// {{{ _modifyManipQuery()
/**
* Changes a manip query string for various DBMS specific reasons
*
* @param string $query query to modify
* @param integer $limit limit the number of rows
* @return string modified query
* @access protected
*/
function _modifyManipQuery($query, $limit)
{
$pos = strpos(strtolower($query), 'where');
$where = $pos ? substr($query, $pos) : '';
$manip_clause = '(\bDELETE\b\s+(?:\*\s+)?\bFROM\b|\bUPDATE\b)';
$from_clause = '([\w\.]+)';
$where_clause = '(?:(.*)\bWHERE\b\s+(.*))|(.*)';
$pattern = '/^'. $manip_clause . '\s+' . $from_clause .'(?:\s)*(?:'. $where_clause .')?$/i';
$matches = preg_match($pattern, $query, $match);
if ($matches) {
$manip = $match[1];
$from = $match[2];
$what = (count($matches) == 6) ? $match[5] : $match[3];
return $manip.' '.$from.' '.$what.' WHERE ctid=(SELECT ctid FROM '.$from.' '.$where.' LIMIT '.$limit.')';
}
//return error?
return $query;
}
// }}}
// {{{ getServerVersion()
/**
* return version information about the server
*
* @param bool $native determines if the raw version string should be returned
* @return mixed array/string with version information or MDB2 error object
* @access public
*/
function getServerVersion($native = false)
{
$query = 'SHOW SERVER_VERSION';
if ($this->connected_server_info) {
$server_info = $this->connected_server_info;
} else {
$server_info = $this->queryOne($query, 'text');
if (PEAR::isError($server_info)) {
return $server_info;
}
}
// cache server_info
$this->connected_server_info = $server_info;
if (!$native && !PEAR::isError($server_info)) {
$tmp = explode('.', $server_info, 3);
if (empty($tmp[2])
&& isset($tmp[1])
&& preg_match('/(\d+)(.*)/', $tmp[1], $tmp2)
) {
$server_info = array(
'major' => $tmp[0],
'minor' => $tmp2[1],
'patch' => null,
'extra' => $tmp2[2],
'native' => $server_info,
);
} else {
$server_info = array(
'major' => isset($tmp[0]) ? $tmp[0] : null,
'minor' => isset($tmp[1]) ? $tmp[1] : null,
'patch' => isset($tmp[2]) ? $tmp[2] : null,
'extra' => null,
'native' => $server_info,
);
}
}
return $server_info;
}
// }}}
// {{{ prepare()
/**
* Prepares a query for multiple execution with execute().
* With some database backends, this is emulated.
* prepare() requires a generic query as string like
* 'INSERT INTO numbers VALUES(?,?)' or
* 'INSERT INTO numbers VALUES(:foo,:bar)'.
* The ? and :name and are placeholders which can be set using
* bindParam() and the query can be sent off using the execute() method.
* The allowed format for :name can be set with the 'bindname_format' option.
*
* @param string $query the query to prepare
* @param mixed $types array that contains the types of the placeholders
* @param mixed $result_types array that contains the types of the columns in
* the result set or MDB2_PREPARE_RESULT, if set to
* MDB2_PREPARE_MANIP the query is handled as a manipulation query
* @param mixed $lobs key (field) value (parameter) pair for all lob placeholders
* @return mixed resource handle for the prepared query on success, a MDB2
* error on failure
* @access public
* @see bindParam, execute
*/
function &prepare($query, $types = null, $result_types = null, $lobs = array())
{
if ($this->options['emulate_prepared']) {
$obj =& parent::prepare($query, $types, $result_types, $lobs);
return $obj;
}
$is_manip = ($result_types === MDB2_PREPARE_MANIP);
$offset = $this->offset;
$limit = $this->limit;
$this->offset = $this->limit = 0;
$result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (PEAR::isError($result)) {
return $result;
}
$query = $result;
}
$pgtypes = function_exists('pg_prepare') ? false : array();
if ($pgtypes !== false && !empty($types)) {
$this->loadModule('Datatype', null, true);
}
$query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
$placeholder_type_guess = $placeholder_type = null;
$question = '?';
$colon = ':';
$positions = array();
$position = $parameter = 0;
while ($position < strlen($query)) {
$q_position = strpos($query, $question, $position);
$c_position = strpos($query, $colon, $position);
//skip "::type" cast ("select id::varchar(20) from sometable where name=?")
$doublecolon_position = strpos($query, '::', $position);
if ($doublecolon_position !== false && $doublecolon_position == $c_position) {
$c_position = strpos($query, $colon, $position+2);
}
if ($q_position && $c_position) {
$p_position = min($q_position, $c_position);
} elseif ($q_position) {
$p_position = $q_position;
} elseif ($c_position) {
$p_position = $c_position;
} else {
break;
}
if (is_null($placeholder_type)) {
$placeholder_type_guess = $query[$p_position];
}
$new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
if (PEAR::isError($new_pos)) {
return $new_pos;
}
if ($new_pos != $position) {
$position = $new_pos;
continue; //evaluate again starting from the new position
}
if ($query[$position] == $placeholder_type_guess) {
if (is_null($placeholder_type)) {
$placeholder_type = $query[$p_position];
$question = $colon = $placeholder_type;
if (!empty($types) && is_array($types)) {
if ($placeholder_type == ':') {
} else {
$types = array_values($types);
}
}
}
if ($placeholder_type_guess == '?') {
$length = 1;
$name = $parameter;
} else {
$regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s';
$param = preg_replace($regexp, '\\1', $query);
if ($param === '') {
$err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
'named parameter name must match "bindname_format" option', __FUNCTION__);
return $err;
}
$length = strlen($param) + 1;
$name = $param;
}
if ($pgtypes !== false) {
if (is_array($types) && array_key_exists($name, $types)) {
$pgtypes[] = $this->datatype->mapPrepareDatatype($types[$name]);
} elseif (is_array($types) && array_key_exists($parameter, $types)) {
$pgtypes[] = $this->datatype->mapPrepareDatatype($types[$parameter]);
} else {
$pgtypes[] = 'text';
}
}
if (($key_parameter = array_search($name, $positions))) {
$next_parameter = 1;
foreach ($positions as $key => $value) {
if ($key_parameter == $key) {
break;
}
++$next_parameter;
}
} else {
++$parameter;
$next_parameter = $parameter;
$positions[] = $name;
}
$query = substr_replace($query, '$'.$parameter, $position, $length);
$position = $p_position + strlen($parameter);
} else {
$position = $p_position;
}
}
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
static $prep_statement_counter = 1;
$statement_name = sprintf($this->options['statement_format'], $this->phptype, $prep_statement_counter++ . sha1(microtime() + mt_rand()));
$statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']);
if ($pgtypes === false) {
$result = @pg_prepare($connection, $statement_name, $query);
if (!$result) {
$err =& $this->raiseError(null, null, null,
'Unable to create prepared statement handle', __FUNCTION__);
return $err;
}
} else {
$types_string = '';
if ($pgtypes) {
$types_string = ' ('.implode(', ', $pgtypes).') ';
}
$query = 'PREPARE '.$statement_name.$types_string.' AS '.$query;
$statement =& $this->_doQuery($query, true, $connection);
if (PEAR::isError($statement)) {
return $statement;
}
}
$class_name = 'MDB2_Statement_'.$this->phptype;
$obj = new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
$this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
return $obj;
}
// }}}
// {{{ function getSequenceName($sqn)
/**
* adds sequence name formatting to a sequence name
*
* @param string name of the sequence
*
* @return string formatted sequence name
*
* @access public
*/
function getSequenceName($sqn)
{
if (false === $this->options['disable_smart_seqname']) {
if (strpos($sqn, '_') !== false) {
list($table, $field) = explode('_', $sqn, 2);
}
$schema_list = $this->queryOne("SELECT array_to_string(current_schemas(false), ',')");
if (PEAR::isError($schema_list) || empty($schema_list) || count($schema_list) < 2) {
$order_by = ' a.attnum';
$schema_clause = ' AND n.nspname=current_schema()';
} else {
$schemas = explode(',', $schema_list);
$schema_clause = ' AND n.nspname IN ('.$schema_list.')';
$counter = 1;
$order_by = ' CASE ';
foreach ($schemas as $schema) {
$order_by .= ' WHEN n.nspname='.$schema.' THEN '.$counter++;
}
$order_by .= ' ELSE '.$counter.' END, a.attnum';
}
$query = "SELECT substring((SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_attrdef d
WHERE d.adrelid = a.attrelid
AND d.adnum = a.attnum
AND a.atthasdef
) FROM 'nextval[^'']*''([^'']*)')
FROM pg_attribute a
LEFT JOIN pg_class c ON c.oid = a.attrelid
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef
LEFT JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE (c.relname = ".$this->quote($sqn, 'text');
if (!empty($field)) {
$query .= " OR (c.relname = ".$this->quote($table, 'text')." AND a.attname = ".$this->quote($field, 'text').")";
}
$query .= " )"
.$schema_clause."
AND NOT a.attisdropped
AND a.attnum > 0
AND pg_get_expr(d.adbin, d.adrelid) LIKE 'nextval%'
ORDER BY ".$order_by;
$seqname = $this->queryOne($query);
if (!PEAR::isError($seqname) && !empty($seqname) && is_string($seqname)) {
return $seqname;
}
}
return parent::getSequenceName($sqn);
}
// }}}
// {{{ nextID()
/**
* Returns the next free id of a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true the sequence is
* automatic created, if it
* not exists
* @return mixed MDB2 Error Object or id
* @access public
*/
function nextID($seq_name, $ondemand = true)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
$query = "SELECT NEXTVAL('$sequence_name')";
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$this->expectError(MDB2_ERROR_NOSUCHTABLE);
$result = $this->queryOne($query, 'integer');
$this->popExpect();
$this->popErrorHandling();
if (PEAR::isError($result)) {
if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
$this->loadModule('Manager', null, true);
$result = $this->manager->createSequence($seq_name);
if (PEAR::isError($result)) {
return $this->raiseError($result, null, null,
'on demand sequence could not be created', __FUNCTION__);
}
return $this->nextId($seq_name, false);
}
}
return $result;
}
// }}}
// {{{ lastInsertID()
/**
* Returns the autoincrement ID if supported or $id or fetches the current
* ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
*
* @param string $table name of the table into which a new row was inserted
* @param string $field name of the field into which a new row was inserted
* @return mixed MDB2 Error Object or id
* @access public
*/
function lastInsertID($table = null, $field = null)
{
if (empty($table) && empty($field)) {
return $this->queryOne('SELECT lastval()', 'integer');
}
$seq = $table.(empty($field) ? '' : '_'.$field);
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq), true);
return $this->queryOne("SELECT currval('$sequence_name')", 'integer');
}
// }}}
// {{{ currID()
/**
* Returns the current id of a sequence
*
* @param string $seq_name name of the sequence
* @return mixed MDB2 Error Object or id
* @access public
*/
function currID($seq_name)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
return $this->queryOne("SELECT last_value FROM $sequence_name", 'integer');
}
}
/**
* MDB2 PostGreSQL result driver
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Result_pgsql extends MDB2_Result_Common
{
// }}}
// {{{ fetchRow()
/**
* Fetch a row and insert the data into an existing array.
*
* @param int $fetchmode how the array data should be indexed
* @param int $rownum number of the row where the data can be found
* @return int data array on success, a MDB2 error on failure
* @access public
*/
function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
{
if (!is_null($rownum)) {
$seek = $this->seek($rownum);
if (PEAR::isError($seek)) {
return $seek;
}
}
if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
$fetchmode = $this->db->fetchmode;
}
if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
$row = @pg_fetch_array($this->result, null, PGSQL_ASSOC);
if (is_array($row)
&& $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
) {
$row = array_change_key_case($row, $this->db->options['field_case']);
}
} else {
$row = @pg_fetch_row($this->result);
}
if (!$row) {
if ($this->result === false) {
$err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
return $err;
}
$null = null;
return $null;
}
$mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
$rtrim = false;
if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
if (empty($this->types)) {
$mode += MDB2_PORTABILITY_RTRIM;
} else {
$rtrim = true;
}
}
if ($mode) {
$this->db->_fixResultArrayValues($row, $mode);
}
if (!empty($this->types)) {
$row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
}
if (!empty($this->values)) {
$this->_assignBindColumns($row);
}
if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
$object_class = $this->db->options['fetch_class'];
if ($object_class == 'stdClass') {
$row = (object) $row;
} else {
$row = &new $object_class($row);
}
}
++$this->rownum;
return $row;
}
// }}}
// {{{ _getColumnNames()
/**
* Retrieve the names of columns returned by the DBMS in a query result.
*
* @return mixed Array variable that holds the names of columns as keys
* or an MDB2 error on failure.
* Some DBMS may not return any columns when the result set
* does not contain any rows.
* @access private
*/
function _getColumnNames()
{
$columns = array();
$numcols = $this->numCols();
if (PEAR::isError($numcols)) {
return $numcols;
}
for ($column = 0; $column < $numcols; $column++) {
$column_name = @pg_field_name($this->result, $column);
$columns[$column_name] = $column;
}
if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$columns = array_change_key_case($columns, $this->db->options['field_case']);
}
return $columns;
}
// }}}
// {{{ numCols()
/**
* Count the number of columns returned by the DBMS in a query result.
*
* @access public
* @return mixed integer value with the number of columns, a MDB2 error
* on failure
*/
function numCols()
{
$cols = @pg_num_fields($this->result);
if (is_null($cols)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return count($this->types);
}
return $this->db->raiseError(null, null, null,
'Could not get column count', __FUNCTION__);
}
return $cols;
}
// }}}
// {{{ nextResult()
/**
* Move the internal result pointer to the next available result
*
* @return true on success, false if there is no more result set or an error object on failure
* @access public
*/
function nextResult()
{
$connection = $this->db->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
if (!($this->result = @pg_get_result($connection))) {
return false;
}
return MDB2_OK;
}
// }}}
// {{{ free()
/**
* Free the internal resources associated with result.
*
* @return boolean true on success, false if result is invalid
* @access public
*/
function free()
{
if (is_resource($this->result) && $this->db->connection) {
$free = @pg_free_result($this->result);
if ($free === false) {
return $this->db->raiseError(null, null, null,
'Could not free result', __FUNCTION__);
}
}
$this->result = false;
return MDB2_OK;
}
}
/**
* MDB2 PostGreSQL buffered result driver
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_BufferedResult_pgsql extends MDB2_Result_pgsql
{
// {{{ seek()
/**
* Seek to a specific row in a result set
*
* @param int $rownum number of the row where the data can be found
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function seek($rownum = 0)
{
if ($this->rownum != ($rownum - 1) && !@pg_result_seek($this->result, $rownum)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return MDB2_OK;
}
return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
}
$this->rownum = $rownum - 1;
return MDB2_OK;
}
// }}}
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return mixed true or false on sucess, a MDB2 error on failure
* @access public
*/
function valid()
{
$numrows = $this->numRows();
if (PEAR::isError($numrows)) {
return $numrows;
}
return $this->rownum < ($numrows - 1);
}
// }}}
// {{{ numRows()
/**
* Returns the number of rows in a result object
*
* @return mixed MDB2 Error Object or the number of rows
* @access public
*/
function numRows()
{
$rows = @pg_num_rows($this->result);
if (is_null($rows)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return 0;
}
return $this->db->raiseError(null, null, null,
'Could not get row count', __FUNCTION__);
}
return $rows;
}
}
/**
* MDB2 PostGreSQL statement driver
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
*/
class MDB2_Statement_pgsql extends MDB2_Statement_Common
{
// {{{ _execute()
/**
* Execute a prepared query statement helper method.
*
* @param mixed $result_class string which specifies which result class to use
* @param mixed $result_wrap_class string which specifies which class to wrap results in
*
* @return mixed MDB2_Result or integer (affected rows) on success,
* a MDB2 error on failure
* @access private
*/
function &_execute($result_class = true, $result_wrap_class = false)
{
if (is_null($this->statement)) {
$result =& parent::_execute($result_class, $result_wrap_class);
return $result;
}
$this->db->last_query = $this->query;
$this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values));
if ($this->db->getOption('disable_query')) {
$result = $this->is_manip ? 0 : null;
return $result;
}
$connection = $this->db->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$query = false;
$parameters = array();
// todo: disabled until pg_execute() bytea issues are cleared up
if (true || !function_exists('pg_execute')) {
$query = 'EXECUTE '.$this->statement;
}
if (!empty($this->positions)) {
foreach ($this->positions as $parameter) {
if (!array_key_exists($parameter, $this->values)) {
return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
}
$value = $this->values[$parameter];
$type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
if (is_resource($value) || $type == 'clob' || $type == 'blob' || $this->db->options['lob_allow_url_include']) {
if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
if ($match[1] == 'file://') {
$value = $match[2];
}
$value = @fopen($value, 'r');
$close = true;
}
if (is_resource($value)) {
$data = '';
while (!@feof($value)) {
$data.= @fread($value, $this->db->options['lob_buffer_length']);
}
if ($close) {
@fclose($value);
}
$value = $data;
}
}
$quoted = $this->db->quote($value, $type, $query);
if (PEAR::isError($quoted)) {
return $quoted;
}
$parameters[] = $quoted;
}
if ($query) {
$query.= ' ('.implode(', ', $parameters).')';
}
}
if (!$query) {
$result = @pg_execute($connection, $this->statement, $parameters);
if (!$result) {
$err =& $this->db->raiseError(null, null, null,
'Unable to execute statement', __FUNCTION__);
return $err;
}
} else {
$result = $this->db->_doQuery($query, $this->is_manip, $connection);
if (PEAR::isError($result)) {
return $result;
}
}
if ($this->is_manip) {
$affected_rows = $this->db->_affectedRows($connection, $result);
return $affected_rows;
}
$result =& $this->db->_wrapResult($result, $this->result_types,
$result_class, $result_wrap_class, $this->limit, $this->offset);
$this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
return $result;
}
// }}}
// {{{ free()
/**
* Release resources allocated for the specified prepared query.
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function free()
{
if (is_null($this->positions)) {
return $this->db->raiseError(MDB2_ERROR, null, null,
'Prepared statement has already been freed', __FUNCTION__);
}
$result = MDB2_OK;
if (!is_null($this->statement)) {
$connection = $this->db->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$query = 'DEALLOCATE PREPARE '.$this->statement;
$result = $this->db->_doQuery($query, true, $connection);
}
parent::free();
return $result;
}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: Common.php,v 1.43 2009/01/14 15:01:21 quipo Exp $
//
/**
* @package MDB2
* @category Database
*/
/**
* These are constants for the tableInfo-function
* they are bitwised or'ed. so if there are more constants to be defined
* in the future, adjust MDB2_TABLEINFO_FULL accordingly
*/
define('MDB2_TABLEINFO_ORDER', 1);
define('MDB2_TABLEINFO_ORDERTABLE', 2);
define('MDB2_TABLEINFO_FULL', 3);
/**
* Base class for the schema reverse engineering module that is extended by each MDB2 driver
*
* To load this module in the MDB2 object:
* $mdb->loadModule('Reverse');
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Reverse_Common extends MDB2_Module_Common
{
// {{{ splitTableSchema()
/**
* Split the "[owner|schema].table" notation into an array
*
* @param string $table [schema and] table name
*
* @return array array(schema, table)
* @access private
*/
function splitTableSchema($table)
{
$ret = array();
if (strpos($table, '.') !== false) {
return explode('.', $table);
}
return array(null, $table);
}
// }}}
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table name of table that should be used in method
* @param string $field name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure.
* The returned array contains an array for each field definition,
* with all or some of these indices, depending on the field data type:
* [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type]
* @access public
*/
function getTableFieldDefinition($table, $field)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table name of table that should be used in method
* @param string $index name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* </pre>
* array (
* [fields] => array (
* [field1name] => array() // one entry per each field covered
* [field2name] => array() // by the index
* [field3name] => array(
* [sorting] => ascending
* )
* )
* );
* </pre>
* @access public
*/
function getTableIndexDefinition($table, $index)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of an constraints into an array
*
* @param string $table name of table that should be used in method
* @param string $index name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* <pre>
* array (
* [primary] => 0
* [unique] => 0
* [foreign] => 1
* [check] => 0
* [fields] => array (
* [field1name] => array() // one entry per each field covered
* [field2name] => array() // by the index
* [field3name] => array(
* [sorting] => ascending
* [position] => 3
* )
* )
* [references] => array(
* [table] => name
* [fields] => array(
* [field1name] => array( //one entry per each referenced field
* [position] => 1
* )
* )
* )
* [deferrable] => 0
* [initiallydeferred] => 0
* [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
* [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
* [match] => SIMPLE|PARTIAL|FULL
* );
* </pre>
* @access public
*/
function getTableConstraintDefinition($table, $index)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ getSequenceDefinition()
/**
* Get the structure of a sequence into an array
*
* @param string $sequence name of sequence that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* <pre>
* array (
* [start] => n
* );
* </pre>
* @access public
*/
function getSequenceDefinition($sequence)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$start = $db->currId($sequence);
if (PEAR::isError($start)) {
return $start;
}
if ($db->supports('current_id')) {
$start++;
} else {
$db->warnings[] = 'database does not support getting current
sequence value, the sequence value was incremented';
}
$definition = array();
if ($start != 1) {
$definition = array('start' => $start);
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* The returned array has this structure:
* <pre>
* array (
* [trigger_name] => 'trigger name',
* [table_name] => 'table name',
* [trigger_body] => 'trigger body definition',
* [trigger_type] => 'BEFORE' | 'AFTER',
* [trigger_event] => 'INSERT' | 'UPDATE' | 'DELETE'
* //or comma separated list of multiple events, when supported
* [trigger_enabled] => true|false
* [trigger_comment] => 'trigger comment',
* );
* </pre>
* The oci8 driver also returns a [when_clause] index.
* @access public
*/
function getTriggerDefinition($trigger)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
// }}}
// {{{ tableInfo()
/**
* Returns information about a table or a result set
*
* The format of the resulting array depends on which <var>$mode</var>
* you select. The sample output below is based on this query:
* <pre>
* SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
* FROM tblFoo
* JOIN tblBar ON tblFoo.fldId = tblBar.fldId
* </pre>
*
* <ul>
* <li>
*
* <kbd>null</kbd> (default)
* <pre>
* [0] => Array (
* [table] => tblFoo
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* [1] => Array (
* [table] => tblFoo
* [name] => fldPhone
* [type] => string
* [len] => 20
* [flags] =>
* )
* [2] => Array (
* [table] => tblBar
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* </pre>
*
* </li><li>
*
* <kbd>MDB2_TABLEINFO_ORDER</kbd>
*
* <p>In addition to the information found in the default output,
* a notation of the number of columns is provided by the
* <samp>num_fields</samp> element while the <samp>order</samp>
* element provides an array with the column names as the keys and
* their location index number (corresponding to the keys in the
* the default output) as the values.</p>
*
* <p>If a result set has identical field names, the last one is
* used.</p>
*
* <pre>
* [num_fields] => 3
* [order] => Array (
* [fldId] => 2
* [fldTrans] => 1
* )
* </pre>
*
* </li><li>
*
* <kbd>MDB2_TABLEINFO_ORDERTABLE</kbd>
*
* <p>Similar to <kbd>MDB2_TABLEINFO_ORDER</kbd> but adds more
* dimensions to the array in which the table names are keys and
* the field names are sub-keys. This is helpful for queries that
* join tables which have identical field names.</p>
*
* <pre>
* [num_fields] => 3
* [ordertable] => Array (
* [tblFoo] => Array (
* [fldId] => 0
* [fldPhone] => 1
* )
* [tblBar] => Array (
* [fldId] => 2
* )
* )
* </pre>
*
* </li>
* </ul>
*
* The <samp>flags</samp> element contains a space separated list
* of extra information about the field. This data is inconsistent
* between DBMS's due to the way each DBMS works.
* + <samp>primary_key</samp>
* + <samp>unique_key</samp>
* + <samp>multiple_key</samp>
* + <samp>not_null</samp>
*
* Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
* elements if <var>$result</var> is a table name. The following DBMS's
* provide full information from queries:
* + fbsql
* + mysql
*
* If the 'portability' option has <samp>MDB2_PORTABILITY_FIX_CASE</samp>
* turned on, the names of tables and fields will be lower or upper cased.
*
* @param object|string $result MDB2_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode either unused or one of the tableInfo modes:
* <kbd>MDB2_TABLEINFO_ORDERTABLE</kbd>,
* <kbd>MDB2_TABLEINFO_ORDER</kbd> or
* <kbd>MDB2_TABLEINFO_FULL</kbd> (which does both).
* These are bitwise, so the first two can be
* combined using <kbd>|</kbd>.
*
* @return array an associative array with the information requested.
* A MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::setOption()
*/
function tableInfo($result, $mode = null)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if (!is_string($result)) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'method not implemented', __FUNCTION__);
}
$db->loadModule('Manager', null, true);
$fields = $db->manager->listTableFields($result);
if (PEAR::isError($fields)) {
return $fields;
}
$flags = array();
$idxname_format = $db->getOption('idxname_format');
$db->setOption('idxname_format', '%s');
$indexes = $db->manager->listTableIndexes($result);
if (PEAR::isError($indexes)) {
$db->setOption('idxname_format', $idxname_format);
return $indexes;
}
foreach ($indexes as $index) {
$definition = $this->getTableIndexDefinition($result, $index);
if (PEAR::isError($definition)) {
$db->setOption('idxname_format', $idxname_format);
return $definition;
}
if (count($definition['fields']) > 1) {
foreach ($definition['fields'] as $field => $sort) {
$flags[$field] = 'multiple_key';
}
}
}
$constraints = $db->manager->listTableConstraints($result);
if (PEAR::isError($constraints)) {
return $constraints;
}
foreach ($constraints as $constraint) {
$definition = $this->getTableConstraintDefinition($result, $constraint);
if (PEAR::isError($definition)) {
$db->setOption('idxname_format', $idxname_format);
return $definition;
}
$flag = !empty($definition['primary'])
? 'primary_key' : (!empty($definition['unique'])
? 'unique_key' : false);
if ($flag) {
foreach ($definition['fields'] as $field => $sort) {
if (empty($flags[$field]) || $flags[$field] != 'primary_key') {
$flags[$field] = $flag;
}
}
}
}
$res = array();
if ($mode) {
$res['num_fields'] = count($fields);
}
foreach ($fields as $i => $field) {
$definition = $this->getTableFieldDefinition($result, $field);
if (PEAR::isError($definition)) {
$db->setOption('idxname_format', $idxname_format);
return $definition;
}
$res[$i] = $definition[0];
$res[$i]['name'] = $field;
$res[$i]['table'] = $result;
$res[$i]['type'] = preg_replace('/^([a-z]+).*$/i', '\\1', trim($definition[0]['nativetype']));
// 'primary_key', 'unique_key', 'multiple_key'
$res[$i]['flags'] = empty($flags[$field]) ? '' : $flags[$field];
// not_null', 'unsigned', 'auto_increment', 'default_[rawencodedvalue]'
if (!empty($res[$i]['notnull'])) {
$res[$i]['flags'].= ' not_null';
}
if (!empty($res[$i]['unsigned'])) {
$res[$i]['flags'].= ' unsigned';
}
if (!empty($res[$i]['auto_increment'])) {
$res[$i]['flags'].= ' autoincrement';
}
if (!empty($res[$i]['default'])) {
$res[$i]['flags'].= ' default_'.rawurlencode($res[$i]['default']);
}
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
$db->setOption('idxname_format', $idxname_format);
return $res;
}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: mysql.php,v 1.80 2008/03/26 21:15:37 quipo Exp $
//
require_once('MDB2/Driver/Reverse/Common.php');
/**
* MDB2 MySQL driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_mysql extends MDB2_Driver_Reverse_Common
{
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (PEAR::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$query = "SHOW FULL COLUMNS FROM $table LIKE ".$db->quote($field_name);
$columns = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($columns)) {
return $columns;
}
foreach ($columns as $column) {
$column = array_change_key_case($column, CASE_LOWER);
$column['name'] = $column['field'];
unset($column['field']);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
if ($field_name == $column['name']) {
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (PEAR::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (empty($column['null']) || $column['null'] !== 'YES') {
$notnull = true;
}
$default = false;
if (array_key_exists('default', $column)) {
$default = $column['default'];
if (is_null($default) && $notnull) {
$default = '';
}
}
$autoincrement = false;
if (!empty($column['extra']) && $column['extra'] == 'auto_increment') {
$autoincrement = true;
}
$collate = null;
if (!empty($column['collation'])) {
$collate = $column['collation'];
$charset = preg_replace('/(.+?)(_.+)?/', '$1', $collate);
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
if (!is_null($length)) {
$definition[0]['length'] = $length;
}
if (!is_null($unsigned)) {
$definition[0]['unsigned'] = $unsigned;
}
if (!is_null($fixed)) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
if ($autoincrement !== false) {
$definition[0]['autoincrement'] = $autoincrement;
}
if (!is_null($collate)) {
$definition[0]['collate'] = $collate;
$definition[0]['charset'] = $charset;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
} elseif ($type == 'timestamp' && $notnull && empty($definition[$key]['default'])) {
$definition[$key]['default'] = '0000-00-00 00:00:00';
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$table = $db->quoteIdentifier($table, true);
$query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */";
$index_name_mdb2 = $db->getIndexName($index_name);
$result = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2)));
if (!PEAR::isError($result) && !is_null($result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$index_name = $index_name_mdb2;
}
$result = $db->query(sprintf($query, $db->quote($index_name)));
if (PEAR::isError($result)) {
return $result;
}
$colpos = 1;
$definition = array();
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$key_name = $row['key_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$key_name = strtolower($key_name);
} else {
$key_name = strtoupper($key_name);
}
}
if ($index_name == $key_name) {
if (!$row['non_unique']) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$index_name . ' is not an existing table index', __FUNCTION__);
}
$column_name = $row['column_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => $colpos++
);
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A'
? 'ascending' : 'descending');
}
}
}
$result->free();
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$index_name . ' is not an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$constraint_name_original = $constraint_name;
$table = $db->quoteIdentifier($table, true);
$query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */";
if (strtolower($constraint_name) != 'primary') {
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$result = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2)));
if (!PEAR::isError($result) && !is_null($result)) {
// apply 'idxname_format' only if the query succeeded, otherwise
// fallback to the given $index_name, without transformation
$constraint_name = $constraint_name_mdb2;
}
}
$result = $db->query(sprintf($query, $db->quote($constraint_name)));
if (PEAR::isError($result)) {
return $result;
}
$colpos = 1;
//default values, eventually overridden
$definition = array(
'primary' => false,
'unique' => false,
'foreign' => false,
'check' => false,
'fields' => array(),
'references' => array(
'table' => '',
'fields' => array(),
),
'onupdate' => '',
'ondelete' => '',
'match' => '',
'deferrable' => false,
'initiallydeferred' => false,
);
while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
$row = array_change_key_case($row, CASE_LOWER);
$key_name = $row['key_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$key_name = strtolower($key_name);
} else {
$key_name = strtoupper($key_name);
}
}
if ($constraint_name == $key_name) {
if ($row['non_unique']) {
//FOREIGN KEY?
return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition);
}
if ($row['key_name'] == 'PRIMARY') {
$definition['primary'] = true;
} elseif (!$row['non_unique']) {
$definition['unique'] = true;
}
$column_name = $row['column_name'];
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column_name = strtolower($column_name);
} else {
$column_name = strtoupper($column_name);
}
}
$definition['fields'][$column_name] = array(
'position' => $colpos++
);
if (!empty($row['collation'])) {
$definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A'
? 'ascending' : 'descending');
}
}
}
$result->free();
if (empty($definition['fields'])) {
return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition);
}
return $definition;
}
// }}}
// {{{ _getTableFKConstraintDefinition()
/**
* Get the FK definition from the CREATE TABLE statement
*
* @param string $table table name
* @param string $constraint_name constraint name
* @param array $definition default values for constraint definition
*
* @return array|PEAR_Error
* @access private
*/
function _getTableFKConstraintDefinition($table, $constraint_name, $definition)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SHOW CREATE TABLE '. $db->escape($table);
$constraint = $db->queryOne($query, 'text', 1);
if (!PEAR::isError($constraint) && !empty($constraint)) {
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$constraint = strtolower($constraint);
} else {
$constraint = strtoupper($constraint);
}
}
$constraint_name_original = $constraint_name;
$constraint_name = $db->getIndexName($constraint_name);
$pattern = '/\bCONSTRAINT\s+'.$constraint_name.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^ ]+) \(([^\)]+)\)/i';
if (!preg_match($pattern, str_replace('`', '', $constraint), $matches)) {
//fallback to original constraint name
$pattern = '/\bCONSTRAINT\s+'.$constraint_name_original.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^ ]+) \(([^\)]+)\)/i';
}
if (preg_match($pattern, str_replace('`', '', $constraint), $matches)) {
$definition['foreign'] = true;
$column_names = explode(',', $matches[1]);
$referenced_cols = explode(',', $matches[3]);
$definition['references'] = array(
'table' => $matches[2],
'fields' => array(),
);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
$colpos = 1;
foreach ($referenced_cols as $column_name) {
$definition['references']['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
$definition['onupdate'] = 'NO ACTION';
$definition['ondelete'] = 'NO ACTION';
$definition['match'] = 'SIMPLE';
return $definition;
}
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = 'SELECT trigger_name,
event_object_table AS table_name,
action_statement AS trigger_body,
action_timing AS trigger_type,
event_manipulation AS trigger_event
FROM information_schema.triggers
WHERE trigger_name = '. $db->quote($trigger, 'text');
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($def)) {
return $def;
}
$def['trigger_comment'] = '';
$def['trigger_enabled'] = true;
return $def;
}
// }}}
// {{{ tableInfo()
/**
* Returns information about a table or a result set
*
* @param object|string $result MDB2_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::setOption()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @mysql_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => $case_func(@mysql_field_table($resource, $i)),
'name' => $case_func(@mysql_field_name($resource, $i)),
'type' => @mysql_field_type($resource, $i),
'length' => @mysql_field_len($resource, $i),
'flags' => @mysql_field_flags($resource, $i),
);
if ($res[$i]['type'] == 'string') {
$res[$i]['type'] = 'char';
} elseif ($res[$i]['type'] == 'unknown') {
$res[$i]['type'] = 'decimal';
}
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (PEAR::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
return $res;
}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Paul Cooper <pgc@ucecom.com> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id: pgsql.php,v 1.75 2008/08/22 16:36:20 quipo Exp $
require_once('MDB2/Driver/Reverse/Common.php');
/**
* MDB2 PostGreSQL driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Paul Cooper <pgc@ucecom.com>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
class MDB2_Driver_Reverse_pgsql extends MDB2_Driver_Reverse_Common
{
// {{{ getTableFieldDefinition()
/**
* Get the structure of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$result = $db->loadModule('Datatype', null, true);
if (PEAR::isError($result)) {
return $result;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT a.attname AS name,
t.typname AS type,
CASE a.attlen
WHEN -1 THEN
CASE t.typname
WHEN 'numeric' THEN (a.atttypmod / 65536)
WHEN 'decimal' THEN (a.atttypmod / 65536)
WHEN 'money' THEN (a.atttypmod / 65536)
ELSE CASE a.atttypmod
WHEN -1 THEN NULL
ELSE a.atttypmod - 4
END
END
ELSE a.attlen
END AS length,
CASE t.typname
WHEN 'numeric' THEN (a.atttypmod % 65536) - 4
WHEN 'decimal' THEN (a.atttypmod % 65536) - 4
WHEN 'money' THEN (a.atttypmod % 65536) - 4
ELSE 0
END AS scale,
a.attnotnull,
a.atttypmod,
a.atthasdef,
(SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_attrdef d
WHERE d.adrelid = a.attrelid
AND d.adnum = a.attnum
AND a.atthasdef
) as default
FROM pg_attribute a,
pg_class c,
pg_type t
WHERE c.relname = ".$db->quote($table, 'text')."
AND a.atttypid = t.oid
AND c.oid = a.attrelid
AND NOT a.attisdropped
AND a.attnum > 0
AND a.attname = ".$db->quote($field_name, 'text')."
ORDER BY a.attnum";
$column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($column)) {
return $column;
}
if (empty($column)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
$column = array_change_key_case($column, CASE_LOWER);
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (PEAR::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (!empty($column['attnotnull']) && $column['attnotnull'] == 't') {
$notnull = true;
}
$default = null;
if ($column['atthasdef'] === 't'
&& !preg_match("/nextval\('([^']+)'/", $column['default'])
) {
$pattern = '/^\'(.*)\'::[\w ]+$/i';
$default = $column['default'];#substr($column['adsrc'], 1, -1);
if (is_null($default) && $notnull) {
$default = '';
} elseif (!empty($default) && preg_match($pattern, $default)) {
//remove data type cast
$default = preg_replace ($pattern, '\\1', $default);
}
}
$autoincrement = false;
if (preg_match("/nextval\('([^']+)'/", $column['default'], $nextvals)) {
$autoincrement = true;
}
$definition[0] = array('notnull' => $notnull, 'nativetype' => $column['type']);
if (!is_null($length)) {
$definition[0]['length'] = $length;
}
if (!is_null($unsigned)) {
$definition[0]['unsigned'] = $unsigned;
}
if (!is_null($fixed)) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
if ($autoincrement !== false) {
$definition[0]['autoincrement'] = $autoincrement;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the structure of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = 'SELECT relname, indkey FROM pg_index, pg_class';
$query.= ' WHERE pg_class.oid = pg_index.indexrelid';
$query.= " AND indisunique != 't' AND indisprimary != 't'";
$query.= ' AND pg_class.relname = %s';
$index_name_mdb2 = $db->getIndexName($index_name);
$row = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($row) || empty($row)) {
// fallback to the given $index_name, without transformation
$row = $db->queryRow(sprintf($query, $db->quote($index_name, 'text')), null, MDB2_FETCHMODE_ASSOC);
}
if (PEAR::isError($row)) {
return $row;
}
if (empty($row)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$row = array_change_key_case($row, CASE_LOWER);
$db->loadModule('Manager', null, true);
$columns = $db->manager->listTableFields($table_name);
$definition = array();
$index_column_numbers = explode(' ', $row['indkey']);
$colpos = 1;
foreach ($index_column_numbers as $number) {
$definition['fields'][$columns[($number - 1)]] = array(
'position' => $colpos++,
'sorting' => 'ascending',
);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the structure of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT c.oid,
c.conname AS constraint_name,
CASE WHEN c.contype = 'c' THEN 1 ELSE 0 END AS \"check\",
CASE WHEN c.contype = 'f' THEN 1 ELSE 0 END AS \"foreign\",
CASE WHEN c.contype = 'p' THEN 1 ELSE 0 END AS \"primary\",
CASE WHEN c.contype = 'u' THEN 1 ELSE 0 END AS \"unique\",
CASE WHEN c.condeferrable = 'f' THEN 0 ELSE 1 END AS deferrable,
CASE WHEN c.condeferred = 'f' THEN 0 ELSE 1 END AS initiallydeferred,
--array_to_string(c.conkey, ' ') AS constraint_key,
t.relname AS table_name,
t2.relname AS references_table,
CASE confupdtype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
END AS onupdate,
CASE confdeltype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
END AS ondelete,
CASE confmatchtype
WHEN 'u' THEN 'UNSPECIFIED'
WHEN 'f' THEN 'FULL'
WHEN 'p' THEN 'PARTIAL'
END AS match,
--array_to_string(c.confkey, ' ') AS fk_constraint_key,
consrc
FROM pg_constraint c
LEFT JOIN pg_class t ON c.conrelid = t.oid
LEFT JOIN pg_class t2 ON c.confrelid = t2.oid
WHERE c.conname = %s
AND t.relname = " . $db->quote($table, 'text');
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($row) || empty($row)) {
// fallback to the given $index_name, without transformation
$constraint_name_mdb2 = $constraint_name;
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
}
if (PEAR::isError($row)) {
return $row;
}
$uniqueIndex = false;
if (empty($row)) {
// We might be looking for a UNIQUE index that was not created
// as a constraint but should be treated as such.
$query = 'SELECT relname AS constraint_name,
indkey,
0 AS "check",
0 AS "foreign",
0 AS "primary",
1 AS "unique",
0 AS deferrable,
0 AS initiallydeferred,
NULL AS references_table,
NULL AS onupdate,
NULL AS ondelete,
NULL AS match
FROM pg_index, pg_class
WHERE pg_class.oid = pg_index.indexrelid
AND indisunique = \'t\'
AND pg_class.relname = %s';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($row) || empty($row)) {
// fallback to the given $index_name, without transformation
$constraint_name_mdb2 = $constraint_name;
$row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
}
if (PEAR::isError($row)) {
return $row;
}
if (empty($row)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$uniqueIndex = true;
}
$row = array_change_key_case($row, CASE_LOWER);
$definition = array(
'primary' => (boolean)$row['primary'],
'unique' => (boolean)$row['unique'],
'foreign' => (boolean)$row['foreign'],
'check' => (boolean)$row['check'],
'fields' => array(),
'references' => array(
'table' => $row['references_table'],
'fields' => array(),
),
'deferrable' => (boolean)$row['deferrable'],
'initiallydeferred' => (boolean)$row['initiallydeferred'],
'onupdate' => $row['onupdate'],
'ondelete' => $row['ondelete'],
'match' => $row['match'],
);
if ($uniqueIndex) {
$db->loadModule('Manager', null, true);
$columns = $db->manager->listTableFields($table_name);
$index_column_numbers = explode(' ', $row['indkey']);
$colpos = 1;
foreach ($index_column_numbers as $number) {
$definition['fields'][$columns[($number - 1)]] = array(
'position' => $colpos++,
'sorting' => 'ascending',
);
}
return $definition;
}
$query = 'SELECT a.attname
FROM pg_constraint c
LEFT JOIN pg_class t ON c.conrelid = t.oid
LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.conkey)
WHERE c.conname = %s
AND t.relname = ' . $db->quote($table, 'text');
$fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
if (PEAR::isError($fields)) {
return $fields;
}
$colpos = 1;
foreach ($fields as $field) {
$definition['fields'][$field] = array(
'position' => $colpos++,
'sorting' => 'ascending',
);
}
if ($definition['foreign']) {
$query = 'SELECT a.attname
FROM pg_constraint c
LEFT JOIN pg_class t ON c.confrelid = t.oid
LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.confkey)
WHERE c.conname = %s
AND t.relname = ' . $db->quote($definition['references']['table'], 'text');
$foreign_fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
if (PEAR::isError($foreign_fields)) {
return $foreign_fields;
}
$colpos = 1;
foreach ($foreign_fields as $foreign_field) {
$definition['references']['fields'][$foreign_field] = array(
'position' => $colpos++,
);
}
}
if ($definition['check']) {
$check_def = $db->queryOne("SELECT pg_get_constraintdef(" . $row['oid'] . ", 't')");
// ...
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*
* @TODO: add support for plsql functions and functions with args
*/
function getTriggerDefinition($trigger)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT trg.tgname AS trigger_name,
tbl.relname AS table_name,
CASE
WHEN p.proname IS NOT NULL THEN 'EXECUTE PROCEDURE ' || p.proname || '();'
ELSE ''
END AS trigger_body,
CASE trg.tgtype & cast(2 as int2)
WHEN 0 THEN 'AFTER'
ELSE 'BEFORE'
END AS trigger_type,
CASE trg.tgtype & cast(28 as int2)
WHEN 16 THEN 'UPDATE'
WHEN 8 THEN 'DELETE'
WHEN 4 THEN 'INSERT'
WHEN 20 THEN 'INSERT, UPDATE'
WHEN 28 THEN 'INSERT, UPDATE, DELETE'
WHEN 24 THEN 'UPDATE, DELETE'
WHEN 12 THEN 'INSERT, DELETE'
END AS trigger_event,
CASE trg.tgenabled
WHEN 'O' THEN 't'
ELSE trg.tgenabled
END AS trigger_enabled,
obj_description(trg.oid, 'pg_trigger') AS trigger_comment
FROM pg_trigger trg,
pg_class tbl,
pg_proc p
WHERE trg.tgrelid = tbl.oid
AND trg.tgfoid = p.oid
AND trg.tgname = ". $db->quote($trigger, 'text');
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
);
return $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
}
// }}}
// {{{ tableInfo()
/**
* Returns information about a table or a result set
*
* NOTE: only supports 'table' and 'flags' if <var>$result</var>
* is a table name.
*
* @param object|string $result MDB2_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
if (!is_resource($resource)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Could not generate result resource', __FUNCTION__);
}
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$case_func = 'strtolower';
} else {
$case_func = 'strtoupper';
}
} else {
$case_func = 'strval';
}
$count = @pg_num_fields($resource);
$res = array();
if ($mode) {
$res['num_fields'] = $count;
}
$db->loadModule('Datatype', null, true);
for ($i = 0; $i < $count; $i++) {
$res[$i] = array(
'table' => function_exists('pg_field_table') ? @pg_field_table($resource, $i) : '',
'name' => $case_func(@pg_field_name($resource, $i)),
'type' => @pg_field_type($resource, $i),
'length' => @pg_field_size($resource, $i),
'flags' => '',
);
$mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
if (PEAR::isError($mdb2type_info)) {
return $mdb2type_info;
}
$res[$i]['mdb2type'] = $mdb2type_info[0][0];
if ($mode & MDB2_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
return $res;
}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Authors: Lukas Smith <smith@pooteeweet.org> |
// | Lorenzo Alberton <l.alberton@quipo.it> |
// +----------------------------------------------------------------------+
//
// $Id: sqlite.php,v 1.80 2008/05/03 10:30:14 quipo Exp $
//
require_once('MDB2/Driver/Reverse/Common.php');
/**
* MDB2 SQlite driver for the schema reverse engineering module
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_Reverse_sqlite extends MDB2_Driver_Reverse_Common
{
/**
* Remove SQL comments from the field definition
*
* @access private
*/
function _removeComments($sql) {
$lines = explode("\n", $sql);
foreach ($lines as $k => $line) {
$pieces = explode('--', $line);
if (count($pieces) > 1 && (substr_count($pieces[0], '\'') % 2) == 0) {
$lines[$k] = substr($line, 0, strpos($line, '--'));
}
}
return implode("\n", $lines);
}
/**
*
*/
function _getTableColumns($sql)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_def = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
// replace the decimal length-places-separator with a colon
$column_def = preg_replace('/(\d),(\d)/', '\1:\2', $column_def);
$column_def = $this->_removeComments($column_def);
$column_sql = explode(',', $column_def);
$columns = array();
$count = count($column_sql);
if ($count == 0) {
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unexpected empty table column definition list', __FUNCTION__);
}
$regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i';
$regexp2 = '/^\s*([^ ]+) +(PRIMARY|UNIQUE|CHECK)$/i';
for ($i=0, $j=0; $i<$count; ++$i) {
if (!preg_match($regexp, trim($column_sql[$i]), $matches)) {
if (!preg_match($regexp2, trim($column_sql[$i]))) {
continue;
}
return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'unexpected table column SQL definition: "'.$column_sql[$i].'"', __FUNCTION__);
}
$columns[$j]['name'] = trim($matches[1], implode('', $db->identifier_quoting));
$columns[$j]['type'] = strtolower($matches[2]);
if (isset($matches[4]) && strlen($matches[4])) {
$columns[$j]['length'] = $matches[4];
}
if (isset($matches[6]) && strlen($matches[6])) {
$columns[$j]['decimal'] = $matches[6];
}
if (isset($matches[8]) && strlen($matches[8])) {
$columns[$j]['unsigned'] = true;
}
if (isset($matches[9]) && strlen($matches[9])) {
$columns[$j]['autoincrement'] = true;
}
if (isset($matches[12]) && strlen($matches[12])) {
$default = $matches[12];
if (strlen($default) && $default[0]=="'") {
$default = str_replace("''", "'", substr($default, 1, strlen($default)-2));
}
if ($default === 'NULL') {
$default = null;
}
$columns[$j]['default'] = $default;
}
if (isset($matches[7]) && strlen($matches[7])) {
$columns[$j]['notnull'] = ($matches[7] === ' NOT NULL');
} else if (isset($matches[9]) && strlen($matches[9])) {
$columns[$j]['notnull'] = ($matches[9] === ' NOT NULL');
} else if (isset($matches[13]) && strlen($matches[13])) {
$columns[$j]['notnull'] = ($matches[13] === ' NOT NULL');
}
++$j;
}
return $columns;
}
// {{{ getTableFieldDefinition()
/**
* Get the stucture of a field into an array
*
* @param string $table_name name of table that should be used in method
* @param string $field_name name of field that should be used in method
* @return mixed data array on success, a MDB2 error on failure.
* The returned array contains an array for each field definition,
* with (some of) these indices:
* [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type]
* @access public
*/
function getTableFieldDefinition($table_name, $field_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$result = $db->loadModule('Datatype', null, true);
if (PEAR::isError($result)) {
return $result;
}
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= 'name='.$db->quote($table, 'text');
}
$sql = $db->queryOne($query);
if (PEAR::isError($sql)) {
return $sql;
}
$columns = $this->_getTableColumns($sql);
foreach ($columns as $column) {
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
if ($db->options['field_case'] == CASE_LOWER) {
$column['name'] = strtolower($column['name']);
} else {
$column['name'] = strtoupper($column['name']);
}
} else {
$column = array_change_key_case($column, $db->options['field_case']);
}
if ($field_name == $column['name']) {
$mapped_datatype = $db->datatype->mapNativeDatatype($column);
if (PEAR::isError($mapped_datatype)) {
return $mapped_datatype;
}
list($types, $length, $unsigned, $fixed) = $mapped_datatype;
$notnull = false;
if (!empty($column['notnull'])) {
$notnull = $column['notnull'];
}
$default = false;
if (array_key_exists('default', $column)) {
$default = $column['default'];
if (is_null($default) && $notnull) {
$default = '';
}
}
$autoincrement = false;
if (!empty($column['autoincrement'])) {
$autoincrement = true;
}
$definition[0] = array(
'notnull' => $notnull,
'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
);
if (!is_null($length)) {
$definition[0]['length'] = $length;
}
if (!is_null($unsigned)) {
$definition[0]['unsigned'] = $unsigned;
}
if (!is_null($fixed)) {
$definition[0]['fixed'] = $fixed;
}
if ($default !== false) {
$definition[0]['default'] = $default;
}
if ($autoincrement !== false) {
$definition[0]['autoincrement'] = $autoincrement;
}
foreach ($types as $key => $type) {
$definition[$key] = $definition[0];
if ($type == 'clob' || $type == 'blob') {
unset($definition[$key]['default']);
}
$definition[$key]['type'] = $type;
$definition[$key]['mdb2type'] = $type;
}
return $definition;
}
}
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table column', __FUNCTION__);
}
// }}}
// {{{ getTableIndexDefinition()
/**
* Get the stucture of an index into an array
*
* @param string $table_name name of table that should be used in method
* @param string $index_name name of index that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableIndexDefinition($table_name, $index_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
} else {
$query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
}
$query.= ' AND sql NOT NULL ORDER BY name';
$index_name_mdb2 = $db->getIndexName($index_name);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($index_name_mdb2), 'text'));
} else {
$qry = sprintf($query, $db->quote($index_name_mdb2, 'text'));
}
$sql = $db->queryOne($qry, 'text');
if (PEAR::isError($sql) || empty($sql)) {
// fallback to the given $index_name, without transformation
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($index_name), 'text'));
} else {
$qry = sprintf($query, $db->quote($index_name, 'text'));
}
$sql = $db->queryOne($qry, 'text');
}
if (PEAR::isError($sql)) {
return $sql;
}
if (!$sql) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$sql = strtolower($sql);
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
$column_names = explode(',', $column_names);
if (preg_match("/^create unique/", $sql)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
$definition = array();
$count = count($column_names);
for ($i=0; $i<$count; ++$i) {
$column_name = strtok($column_names[$i], ' ');
$collation = strtok(' ');
$definition['fields'][$column_name] = array(
'position' => $i+1
);
if (!empty($collation)) {
$definition['fields'][$column_name]['sorting'] =
($collation=='ASC' ? 'ascending' : 'descending');
}
}
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing table index', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTableConstraintDefinition()
/**
* Get the stucture of a constraint into an array
*
* @param string $table_name name of table that should be used in method
* @param string $constraint_name name of constraint that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTableConstraintDefinition($table_name, $constraint_name)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
list($schema, $table) = $this->splitTableSchema($table_name);
$query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
} else {
$query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
}
$query.= ' AND sql NOT NULL ORDER BY name';
$constraint_name_mdb2 = $db->getIndexName($constraint_name);
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($constraint_name_mdb2), 'text'));
} else {
$qry = sprintf($query, $db->quote($constraint_name_mdb2, 'text'));
}
$sql = $db->queryOne($qry, 'text');
if (PEAR::isError($sql) || empty($sql)) {
// fallback to the given $index_name, without transformation
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$qry = sprintf($query, $db->quote(strtolower($constraint_name), 'text'));
} else {
$qry = sprintf($query, $db->quote($constraint_name, 'text'));
}
$sql = $db->queryOne($qry, 'text');
}
if (PEAR::isError($sql)) {
return $sql;
}
//default values, eventually overridden
$definition = array(
'primary' => false,
'unique' => false,
'foreign' => false,
'check' => false,
'fields' => array(),
'references' => array(
'table' => '',
'fields' => array(),
),
'onupdate' => '',
'ondelete' => '',
'match' => '',
'deferrable' => false,
'initiallydeferred' => false,
);
if (!$sql) {
$query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
} else {
$query.= 'name='.$db->quote($table, 'text');
}
$query.= " AND sql NOT NULL ORDER BY name";
$sql = $db->queryOne($query, 'text');
if (PEAR::isError($sql)) {
return $sql;
}
if ($constraint_name == 'primary') {
// search in table definition for PRIMARY KEYs
if (preg_match("/\bPRIMARY\s+KEY\b\s*\(([^)]+)/i", $sql, $tmp)) {
$definition['primary'] = true;
$definition['fields'] = array();
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
return $definition;
}
if (preg_match("/\"([^\"]+)\"[^\,\"]+\bPRIMARY\s+KEY\b[^\,\)]*/i", $sql, $tmp)) {
$definition['primary'] = true;
$definition['fields'] = array();
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
return $definition;
}
} else {
// search in table definition for FOREIGN KEYs
$pattern = "/\bCONSTRAINT\b\s+%s\s+
\bFOREIGN\s+KEY\b\s*\(([^\)]+)\)\s*
\bREFERENCES\b\s+([^\s]+)\s*\(([^\)]+)\)\s*
(?:\bMATCH\s*([^\s]+))?\s*
(?:\bON\s+UPDATE\s+([^\s,\)]+))?\s*
(?:\bON\s+DELETE\s+([^\s,\)]+))?\s*
/imsx";
$found_fk = false;
if (preg_match(sprintf($pattern, $constraint_name_mdb2), $sql, $tmp)) {
$found_fk = true;
} elseif (preg_match(sprintf($pattern, $constraint_name), $sql, $tmp)) {
$found_fk = true;
}
if ($found_fk) {
$definition['foreign'] = true;
$definition['match'] = 'SIMPLE';
$definition['onupdate'] = 'NO ACTION';
$definition['ondelete'] = 'NO ACTION';
$definition['references']['table'] = $tmp[2];
$column_names = explode(',', $tmp[1]);
$colpos = 1;
foreach ($column_names as $column_name) {
$definition['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
$referenced_cols = explode(',', $tmp[3]);
$colpos = 1;
foreach ($referenced_cols as $column_name) {
$definition['references']['fields'][trim($column_name)] = array(
'position' => $colpos++
);
}
if (isset($tmp[4])) {
$definition['match'] = $tmp[4];
}
if (isset($tmp[5])) {
$definition['onupdate'] = $tmp[5];
}
if (isset($tmp[6])) {
$definition['ondelete'] = $tmp[6];
}
return $definition;
}
}
$sql = false;
}
if (!$sql) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$sql = strtolower($sql);
$start_pos = strpos($sql, '(');
$end_pos = strrpos($sql, ')');
$column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
$column_names = explode(',', $column_names);
if (!preg_match("/^create unique/", $sql)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
$definition['unique'] = true;
$count = count($column_names);
for ($i=0; $i<$count; ++$i) {
$column_name = strtok($column_names[$i]," ");
$collation = strtok(" ");
$definition['fields'][$column_name] = array(
'position' => $i+1
);
if (!empty($collation)) {
$definition['fields'][$column_name]['sorting'] =
($collation=='ASC' ? 'ascending' : 'descending');
}
}
if (empty($definition['fields'])) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
$constraint_name . ' is not an existing table constraint', __FUNCTION__);
}
return $definition;
}
// }}}
// {{{ getTriggerDefinition()
/**
* Get the structure of a trigger into an array
*
* EXPERIMENTAL
*
* WARNING: this function is experimental and may change the returned value
* at any time until labelled as non-experimental
*
* @param string $trigger name of trigger that should be used in method
* @return mixed data array on success, a MDB2 error on failure
* @access public
*/
function getTriggerDefinition($trigger)
{
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$query = "SELECT name as trigger_name,
tbl_name AS table_name,
sql AS trigger_body,
NULL AS trigger_type,
NULL AS trigger_event,
NULL AS trigger_comment,
1 AS trigger_enabled
FROM sqlite_master
WHERE type='trigger'";
if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$query.= ' AND LOWER(name)='.$db->quote(strtolower($trigger), 'text');
} else {
$query.= ' AND name='.$db->quote($trigger, 'text');
}
$types = array(
'trigger_name' => 'text',
'table_name' => 'text',
'trigger_body' => 'text',
'trigger_type' => 'text',
'trigger_event' => 'text',
'trigger_comment' => 'text',
'trigger_enabled' => 'boolean',
);
$def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($def)) {
return $def;
}
if (empty($def)) {
return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'it was not specified an existing trigger', __FUNCTION__);
}
if (preg_match("/^create\s+(?:temp|temporary)?trigger\s+(?:if\s+not\s+exists\s+)?.*(before|after)?\s+(insert|update|delete)/Uims", $def['trigger_body'], $tmp)) {
$def['trigger_type'] = strtoupper($tmp[1]);
$def['trigger_event'] = strtoupper($tmp[2]);
}
return $def;
}
// }}}
// {{{ tableInfo()
/**
* Returns information about a table
*
* @param string $result a string containing the name of a table
* @param int $mode a valid tableInfo mode
*
* @return array an associative array with the information requested.
* A MDB2_Error object on failure.
*
* @see MDB2_Driver_Common::tableInfo()
* @since Method available since Release 1.7.0
*/
function tableInfo($result, $mode = null)
{
if (is_string($result)) {
return parent::tableInfo($result, $mode);
}
$db =$this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null,
'This DBMS can not obtain tableInfo from result sets', __FUNCTION__);
}
}
?>
<?php
// vim: set et ts=4 sw=4 fdm=marker:
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: sqlite.php,v 1.165 2008/11/30 14:28:01 afz Exp $
//
/**
* MDB2 SQLite driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Driver_sqlite extends MDB2_Driver_Common
{
// {{{ properties
var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false);
var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
var $_lasterror = '';
var $fix_assoc_fields_names = false;
// }}}
// {{{ constructor
/**
* Constructor
*/
function __construct()
{
parent::__construct();
$this->phptype = 'sqlite';
$this->dbsyntax = 'sqlite';
$this->supported['sequences'] = 'emulated';
$this->supported['indexes'] = true;
$this->supported['affected_rows'] = true;
$this->supported['summary_functions'] = true;
$this->supported['order_by_text'] = true;
$this->supported['current_id'] = 'emulated';
$this->supported['limit_queries'] = true;
$this->supported['LOBs'] = true;
$this->supported['replace'] = true;
$this->supported['transactions'] = true;
$this->supported['savepoints'] = false;
$this->supported['sub_selects'] = true;
$this->supported['triggers'] = true;
$this->supported['auto_increment'] = true;
$this->supported['primary_key'] = false; // requires alter table implementation
$this->supported['result_introspection'] = false; // not implemented
$this->supported['prepared_statements'] = 'emulated';
$this->supported['identifier_quoting'] = true;
$this->supported['pattern_escaping'] = false;
$this->supported['new_link'] = false;
$this->options['DBA_username'] = false;
$this->options['DBA_password'] = false;
$this->options['base_transaction_name'] = '___php_MDB2_sqlite_auto_commit_off';
$this->options['fixed_float'] = 0;
$this->options['database_path'] = '';
$this->options['database_extension'] = '';
$this->options['server_version'] = '';
$this->options['max_identifiers_length'] = 128; //no real limit
}
// }}}
// {{{ errorInfo()
/**
* This method is used to collect information about an error
*
* @param integer $error
* @return array
* @access public
*/
function errorInfo($error = null)
{
$native_code = null;
if ($this->connection) {
$native_code = @sqlite_last_error($this->connection);
}
$native_msg = $this->_lasterror
? html_entity_decode($this->_lasterror) : @sqlite_error_string($native_code);
// PHP 5.2+ prepends the function name to $php_errormsg, so we need
// this hack to work around it, per bug #9599.
$native_msg = preg_replace('/^sqlite[a-z_]+\(\)[^:]*: /', '', $native_msg);
if (is_null($error)) {
static $error_regexps;
if (empty($error_regexps)) {
$error_regexps = array(
'/^no such table:/' => MDB2_ERROR_NOSUCHTABLE,
'/^no such index:/' => MDB2_ERROR_NOT_FOUND,
'/^(table|index) .* already exists$/' => MDB2_ERROR_ALREADY_EXISTS,
'/PRIMARY KEY must be unique/i' => MDB2_ERROR_CONSTRAINT,
'/is not unique/' => MDB2_ERROR_CONSTRAINT,
'/columns .* are not unique/i' => MDB2_ERROR_CONSTRAINT,
'/uniqueness constraint failed/' => MDB2_ERROR_CONSTRAINT,
'/may not be NULL/' => MDB2_ERROR_CONSTRAINT_NOT_NULL,
'/^no such column:/' => MDB2_ERROR_NOSUCHFIELD,
'/no column named/' => MDB2_ERROR_NOSUCHFIELD,
'/column not present in both tables/i' => MDB2_ERROR_NOSUCHFIELD,
'/^near ".*": syntax error$/' => MDB2_ERROR_SYNTAX,
'/[0-9]+ values for [0-9]+ columns/i' => MDB2_ERROR_VALUE_COUNT_ON_ROW,
);
}
foreach ($error_regexps as $regexp => $code) {
if (preg_match($regexp, $native_msg)) {
$error = $code;
break;
}
}
}
return array($error, $native_code, $native_msg);
}
// }}}
// {{{ escape()
/**
* Quotes a string so it can be safely used in a query. It will quote
* the text so it can safely be used within a query.
*
* @param string the input string to quote
* @param bool escape wildcards
*
* @return string quoted string
*
* @access public
*/
function escape($text, $escape_wildcards = false)
{
$text = @sqlite_escape_string($text);
return $text;
}
// }}}
// {{{ beginTransaction()
/**
* Start a transaction or set a savepoint.
*
* @param string name of a savepoint to set
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function beginTransaction($savepoint = null)
{
$this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!is_null($savepoint)) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
} elseif ($this->in_transaction) {
return MDB2_OK; //nothing to do
}
if (!$this->destructor_registered && $this->opened_persistent) {
$this->destructor_registered = true;
register_shutdown_function('MDB2_closeOpenTransactions');
}
$query = 'BEGIN TRANSACTION '.$this->options['base_transaction_name'];
$result =$this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
$this->in_transaction = true;
return MDB2_OK;
}
// }}}
// {{{ commit()
/**
* Commit the database changes done during a transaction that is in
* progress or release a savepoint. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after committing the pending changes.
*
* @param string name of a savepoint to release
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function commit($savepoint = null)
{
$this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
}
if (!is_null($savepoint)) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
$query = 'COMMIT TRANSACTION '.$this->options['base_transaction_name'];
$result =$this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{
/**
* Cancel any database changes done during a transaction or since a specific
* savepoint that is in progress. This function may only be called when
* auto-committing is disabled, otherwise it will fail. Therefore, a new
* transaction is implicitly started after canceling the pending changes.
*
* @param string name of a savepoint to rollback to
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
*/
function rollback($savepoint = null)
{
$this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
if (!$this->in_transaction) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'rollback cannot be done changes are auto committed', __FUNCTION__);
}
if (!is_null($savepoint)) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'savepoints are not supported', __FUNCTION__);
}
$query = 'ROLLBACK TRANSACTION '.$this->options['base_transaction_name'];
$result =$this->_doQuery($query, true);
if (PEAR::isError($result)) {
return $result;
}
$this->in_transaction = false;
return MDB2_OK;
}
// }}}
// {{{ function setTransactionIsolation()
/**
* Set the transacton isolation level.
*
* @param string standard isolation level
* READ UNCOMMITTED (allows dirty reads)
* READ COMMITTED (prevents dirty reads)
* REPEATABLE READ (prevents nonrepeatable reads)
* SERIALIZABLE (prevents phantom reads)
* @return mixed MDB2_OK on success, a MDB2 error on failure
*
* @access public
* @since 2.1.1
*/
static function setTransactionIsolation($isolation,$options=array())
{
$this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
switch ($isolation) {
case 'READ UNCOMMITTED':
$isolation = 0;
break;
case 'READ COMMITTED':
case 'REPEATABLE READ':
case 'SERIALIZABLE':
$isolation = 1;
break;
default:
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'isolation level is not supported: '.$isolation, __FUNCTION__);
}
$query = "PRAGMA read_uncommitted=$isolation";
return $this->_doQuery($query, true);
}
// }}}
// {{{ getDatabaseFile()
/**
* Builds the string with path+dbname+extension
*
* @return string full database path+file
* @access protected
*/
function _getDatabaseFile($database_name)
{
if ($database_name === '' || $database_name === ':memory:') {
return $database_name;
}
return $this->options['database_path'].$database_name.$this->options['database_extension'];
}
// }}}
// {{{ connect()
/**
* Connect to the database
*
* @return true on success, MDB2 Error Object on failure
**/
function connect()
{
$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" );
$database_file = $this->_getDatabaseFile($this->database_name);
if (is_resource($this->connection)) {
//if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
if (MDB2::areEquals($this->connected_dsn, $this->dsn)
&& $this->connected_database_name == $database_file
&& $this->opened_persistent == $this->options['persistent']
) {
return MDB2_OK;
}
$this->disconnect(false);
}
if (!PEAR::loadExtension($this->phptype)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
}
if (empty($this->database_name)) {
return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
'unable to establish a connection', __FUNCTION__);
}
if ($database_file !== ':memory:') {
if(!strpos($database_file,'.db')){
$database_file="$datadir/$database_file.db";
}
if (!file_exists($database_file)) {
if (!touch($database_file)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Could not create database file', __FUNCTION__);
}
if (!isset($this->dsn['mode'])
|| !is_numeric($this->dsn['mode'])
) {
$mode = 0644;
} else {
$mode = octdec($this->dsn['mode']);
}
if (!chmod($database_file, $mode)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Could not be chmodded database file', __FUNCTION__);
}
if (!file_exists($database_file)) {
return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
'Could not be found database file', __FUNCTION__);
}
}
if (!is_file($database_file)) {
return $this->raiseError(MDB2_ERROR_INVALID, null, null,
'Database is a directory name', __FUNCTION__);
}
if (!is_readable($database_file)) {
return $this->raiseError(MDB2_ERROR_ACCESS_VIOLATION, null, null,
'Could not read database file', __FUNCTION__);
}
}
$connect_function = ($this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open');
$php_errormsg = '';
if (version_compare('5.1.0', PHP_VERSION, '>')) {
@ini_set('track_errors', true);
echo 1;
$connection = @$connect_function($database_file);
echo 2;
@ini_restore('track_errors');
} else {
$connection = @$connect_function($database_file, 0666, $php_errormsg);
}
$this->_lasterror = $php_errormsg;
if (!$connection) {
return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
'unable to establish a connection', __FUNCTION__);
}
if ($this->fix_assoc_fields_names ||
$this->options['portability'] & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES)
{
@sqlite_query("PRAGMA short_column_names = 1", $connection);
$this->fix_assoc_fields_names = true;
}
$this->connection = $connection;
$this->connected_dsn = $this->dsn;
$this->connected_database_name = $database_file;
$this->opened_persistent = $this->getoption('persistent');
$this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
return MDB2_OK;
}
// }}}
// {{{ databaseExists()
/**
* check if given database name is exists?
*
* @param string $name name of the database that should be checked
*
* @return mixed true/false on success, a MDB2 error on failure
* @access public
*/
function databaseExists($name)
{
$database_file = $this->_getDatabaseFile($name);
$result = file_exists($database_file);
return $result;
}
// }}}
// {{{ disconnect()
/**
* Log out and disconnect from the database.
*
* @param boolean $force if the disconnect should be forced even if the
* connection is opened persistently
* @return mixed true on success, false if not connected and error
* object on error
* @access public
*/
function disconnect($force = true)
{
if (is_resource($this->connection)) {
if ($this->in_transaction) {
$dsn = $this->dsn;
$database_name = $this->database_name;
$persistent = $this->options['persistent'];
$this->dsn = $this->connected_dsn;
$this->database_name = $this->connected_database_name;
$this->options['persistent'] = $this->opened_persistent;
$this->rollback();
$this->dsn = $dsn;
$this->database_name = $database_name;
$this->options['persistent'] = $persistent;
}
if (!$this->opened_persistent || $force) {
@sqlite_close($this->connection);
}
} else {
return false;
}
return parent::disconnect($force);
}
// }}}
// {{{ _doQuery()
/**
* Execute a query
* @param string $query query
* @param boolean $is_manip if the query is a manipulation query
* @param resource $connection
* @param string $database_name
* @return result or error object
* @access protected
*/
function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
{
$this->last_query = $query;
$result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
if ($result) {
if (PEAR::isError($result)) {
return $result;
}
$query = $result;
}
if ($this->options['disable_query']) {
$result = $is_manip ? 0 : null;
return $result;
}
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
$function = $this->options['result_buffering']
? 'sqlite_query' : 'sqlite_unbuffered_query';
$php_errormsg = '';
if (version_compare('5.1.0', PHP_VERSION, '>')) {
@ini_set('track_errors', true);
do {
$result = @$function($query.';', $connection);
} while (sqlite_last_error($connection) == SQLITE_SCHEMA);
@ini_restore('track_errors');
} else {
do {
$result = @$function($query.';', $connection, SQLITE_BOTH, $php_errormsg);
} while (sqlite_last_error($connection) == SQLITE_SCHEMA);
}
$this->_lasterror = $php_errormsg;
if (!$result) {
$err =$this->raiseError(null, null, null,
'Could not execute statement', __FUNCTION__);
return $err;
}
$this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
return $result;
}
// }}}
// {{{ _affectedRows()
/**
* Returns the number of rows affected
*
* @param resource $result
* @param resource $connection
* @return mixed MDB2 Error Object or the number of rows affected
* @access private
*/
function _affectedRows($connection, $result = null)
{
if (is_null($connection)) {
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
}
return @sqlite_changes($connection);
}
// }}}
// {{{ _modifyQuery()
/**
* Changes a query string for various DBMS specific reasons
*
* @param string $query query to modify
* @param boolean $is_manip if it is a DML query
* @param integer $limit limit the number of rows
* @param integer $offset start reading from given offset
* @return string modified query
* @access protected
*/
function _modifyQuery($query, $is_manip, $limit, $offset)
{
if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) {
if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
$query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
'DELETE FROM \1 WHERE 1=1', $query);
}
}
if ($limit > 0
&& !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query)
) {
$query = rtrim($query);
if (substr($query, -1) == ';') {
$query = substr($query, 0, -1);
}
if ($is_manip) {
$query.= " LIMIT $limit";
} else {
$query.= " LIMIT $offset,$limit";
}
}
return $query;
}
// }}}
// {{{ getServerVersion()
/**
* return version information about the server
*
* @param bool $native determines if the raw version string should be returned
* @return mixed array/string with version information or MDB2 error object
* @access public
*/
function getServerVersion($native = false)
{
$server_info = false;
if ($this->connected_server_info) {
$server_info = $this->connected_server_info;
} elseif ($this->options['server_version']) {
$server_info = $this->options['server_version'];
} elseif (function_exists('sqlite_libversion')) {
$server_info = @sqlite_libversion();
}
if (!$server_info) {
return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
'Requires either the "server_version" option or the sqlite_libversion() function', __FUNCTION__);
}
// cache server_info
$this->connected_server_info = $server_info;
if (!$native) {
$tmp = explode('.', $server_info, 3);
$server_info = array(
'major' => isset($tmp[0]) ? $tmp[0] : null,
'minor' => isset($tmp[1]) ? $tmp[1] : null,
'patch' => isset($tmp[2]) ? $tmp[2] : null,
'extra' => null,
'native' => $server_info,
);
}
return $server_info;
}
// }}}
// {{{ replace()
/**
* Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
* query, except that if there is already a row in the table with the same
* key field values, the old row is deleted before the new row is inserted.
*
* The REPLACE type of query does not make part of the SQL standards. Since
* practically only SQLite implements it natively, this type of query is
* emulated through this method for other DBMS using standard types of
* queries inside a transaction to assure the atomicity of the operation.
*
* @access public
*
* @param string $table name of the table on which the REPLACE query will
* be executed.
* @param array $fields associative array that describes the fields and the
* values that will be inserted or updated in the specified table. The
* indexes of the array are the names of all the fields of the table. The
* values of the array are also associative arrays that describe the
* values and other properties of the table fields.
*
* Here follows a list of field properties that need to be specified:
*
* value:
* Value to be assigned to the specified field. This value may be
* of specified in database independent type format as this
* function can perform the necessary datatype conversions.
*
* Default:
* this property is required unless the Null property
* is set to 1.
*
* type
* Name of the type of the field. Currently, all types Metabase
* are supported except for clob and blob.
*
* Default: no type conversion
*
* null
* Boolean property that indicates that the value for this field
* should be set to null.
*
* The default value for fields missing in INSERT queries may be
* specified the definition of a table. Often, the default value
* is already null, but since the REPLACE may be emulated using
* an UPDATE query, make sure that all fields of the table are
* listed in this function argument array.
*
* Default: 0
*
* key
* Boolean property that indicates that this field should be
* handled as a primary key or at least as part of the compound
* unique index of the table that will determine the row that will
* updated if it exists or inserted a new row otherwise.
*
* This function will fail if no key field is specified or if the
* value of a key field is set to null because fields that are
* part of unique index they may not be null.
*
* Default: 0
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
*/
function replace($table, $fields)
{
$count = count($fields);
$query = $values = '';
$keys = $colnum = 0;
for (reset($fields); $colnum < $count; next($fields), $colnum++) {
$name = key($fields);
if ($colnum > 0) {
$query .= ',';
$values.= ',';
}
$query.= $this->quoteIdentifier($name, true);
if (isset($fields[$name]['null']) && $fields[$name]['null']) {
$value = 'NULL';
} else {
$type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
$value = $this->quote($fields[$name]['value'], $type);
if (PEAR::isError($value)) {
return $value;
}
}
$values.= $value;
if (isset($fields[$name]['key']) && $fields[$name]['key']) {
if ($value === 'NULL') {
return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
'key value '.$name.' may not be NULL', __FUNCTION__);
}
$keys++;
}
}
if ($keys == 0) {
return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
'not specified which fields are keys', __FUNCTION__);
}
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$table = $this->quoteIdentifier($table, true);
$query = "REPLACE INTO $table ($query) VALUES ($values)";
$result =$this->_doQuery($query, true, $connection);
if (PEAR::isError($result)) {
return $result;
}
return $this->_affectedRows($connection, $result);
}
// }}}
// {{{ nextID()
/**
* Returns the next free id of a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true the sequence is
* automatic created, if it
* not exists
*
* @return mixed MDB2 Error Object or id
* @access public
*/
function nextID($seq_name, $ondemand = true)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
$seqcol_name = $this->options['seqcol_name'];
$query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)";
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$this->expectError(MDB2_ERROR_NOSUCHTABLE);
$result =$this->_doQuery($query, true);
$this->popExpect();
$this->popErrorHandling();
if (PEAR::isError($result)) {
if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
$this->loadModule('Manager', null, true);
$result = $this->manager->createSequence($seq_name);
if (PEAR::isError($result)) {
return $this->raiseError($result, null, null,
'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
} else {
return $this->nextID($seq_name, false);
}
}
return $result;
}
$value = $this->lastInsertID();
if (is_numeric($value)) {
$query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
$result =$this->_doQuery($query, true);
if (PEAR::isError($result)) {
$this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
}
}
return $value;
}
// }}}
// {{{ lastInsertID()
/**
* Returns the autoincrement ID if supported or $id or fetches the current
* ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
*
* @param string $table name of the table into which a new row was inserted
* @param string $field name of the field into which a new row was inserted
* @return mixed MDB2 Error Object or id
* @access public
*/
function lastInsertID($table = null, $field = null)
{
$connection = $this->getConnection();
if (PEAR::isError($connection)) {
return $connection;
}
$value = @sqlite_last_insert_rowid($connection);
if (!$value) {
return $this->raiseError(null, null, null,
'Could not get last insert ID', __FUNCTION__);
}
return $value;
}
// }}}
// {{{ currID()
/**
* Returns the current id of a sequence
*
* @param string $seq_name name of the sequence
* @return mixed MDB2 Error Object or id
* @access public
*/
function currID($seq_name)
{
$sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
$seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
$query = "SELECT MAX($seqcol_name) FROM $sequence_name";
return $this->queryOne($query, 'integer');
}
}
/**
* MDB2 SQLite result driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Result_sqlite extends MDB2_Result_Common
{
// }}}
// {{{ fetchRow()
/**
* Fetch a row and insert the data into an existing array.
*
* @param int $fetchmode how the array data should be indexed
* @param int $rownum number of the row where the data can be found
* @return int data array on success, a MDB2 error on failure
* @access public
*/
function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
{
if (!is_null($rownum)) {
$seek = $this->seek($rownum);
if (PEAR::isError($seek)) {
return $seek;
}
}
if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
$fetchmode = $this->db->fetchmode;
}
if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
$row = @sqlite_fetch_array($this->result, SQLITE_ASSOC);
if (is_array($row)
&& $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
) {
$row = array_change_key_case($row, $this->db->options['field_case']);
}
} else {
$row = @sqlite_fetch_array($this->result, SQLITE_NUM);
}
if (!$row) {
if ($this->result === false) {
$err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
return $err;
}
$null = null;
return $null;
}
$mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
$rtrim = false;
if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
if (empty($this->types)) {
$mode += MDB2_PORTABILITY_RTRIM;
} else {
$rtrim = true;
}
}
if ($mode) {
$this->db->_fixResultArrayValues($row, $mode);
}
if (!empty($this->types)) {
$row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
}
if (!empty($this->values)) {
$this->_assignBindColumns($row);
}
if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
$object_class = $this->db->options['fetch_class'];
if ($object_class == 'stdClass') {
$row = (object) $row;
} else {
$row = new $object_class($row);
}
}
++$this->rownum;
return $row;
}
// }}}
// {{{ _getColumnNames()
/**
* Retrieve the names of columns returned by the DBMS in a query result.
*
* @return mixed Array variable that holds the names of columns as keys
* or an MDB2 error on failure.
* Some DBMS may not return any columns when the result set
* does not contain any rows.
* @access private
*/
function _getColumnNames()
{
$columns = array();
$numcols = $this->numCols();
if (PEAR::isError($numcols)) {
return $numcols;
}
for ($column = 0; $column < $numcols; $column++) {
$column_name = @sqlite_field_name($this->result, $column);
$columns[$column_name] = $column;
}
if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$columns = array_change_key_case($columns, $this->db->options['field_case']);
}
return $columns;
}
// }}}
// {{{ numCols()
/**
* Count the number of columns returned by the DBMS in a query result.
*
* @access public
* @return mixed integer value with the number of columns, a MDB2 error
* on failure
*/
function numCols()
{
$cols = @sqlite_num_fields($this->result);
if (is_null($cols)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return count($this->types);
}
return $this->db->raiseError(null, null, null,
'Could not get column count', __FUNCTION__);
}
return $cols;
}
}
/**
* MDB2 SQLite buffered result driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_BufferedResult_sqlite extends MDB2_Result_sqlite
{
// {{{ seek()
/**
* Seek to a specific row in a result set
*
* @param int $rownum number of the row where the data can be found
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
*/
function seek($rownum = 0)
{
if (!@sqlite_seek($this->result, $rownum)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return MDB2_OK;
}
return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
}
$this->rownum = $rownum - 1;
return MDB2_OK;
}
// }}}
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return mixed true or false on sucess, a MDB2 error on failure
* @access public
*/
function valid()
{
$numrows = $this->numRows();
if (PEAR::isError($numrows)) {
return $numrows;
}
return $this->rownum < ($numrows - 1);
}
// }}}
// {{{ numRows()
/**
* Returns the number of rows in a result object
*
* @return mixed MDB2 Error Object or the number of rows
* @access public
*/
function numRows()
{
$rows = @sqlite_num_rows($this->result);
if (is_null($rows)) {
if ($this->result === false) {
return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'resultset has already been freed', __FUNCTION__);
} elseif (is_null($this->result)) {
return 0;
}
return $this->db->raiseError(null, null, null,
'Could not get row count', __FUNCTION__);
}
return $rows;
}
}
/**
* MDB2 SQLite statement driver
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Statement_sqlite extends MDB2_Statement_Common
{
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: Extended.php,v 1.60 2007/11/28 19:49:34 quipo Exp $
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
/**
* Used by autoPrepare()
*/
define('MDB2_AUTOQUERY_INSERT', 1);
define('MDB2_AUTOQUERY_UPDATE', 2);
define('MDB2_AUTOQUERY_DELETE', 3);
define('MDB2_AUTOQUERY_SELECT', 4);
/**
* MDB2_Extended: class which adds several high level methods to MDB2
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Extended extends MDB2_Module_Common
{
// {{{ autoPrepare()
/**
* Generate an insert, update or delete query and call prepare() on it
*
* @param string table
* @param array the fields names
* @param int type of query to build
* MDB2_AUTOQUERY_INSERT
* MDB2_AUTOQUERY_UPDATE
* MDB2_AUTOQUERY_DELETE
* MDB2_AUTOQUERY_SELECT
* @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
* @param array that contains the types of the placeholders
* @param mixed array that contains the types of the columns in
* the result set or MDB2_PREPARE_RESULT, if set to
* MDB2_PREPARE_MANIP the query is handled as a manipulation query
*
* @return resource handle for the query
* @see buildManipSQL
* @access public
*/
function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT,
$where = false, $types = null, $result_types = MDB2_PREPARE_MANIP)
{
$query = $this->buildManipSQL($table, $table_fields, $mode, $where);
if (PEAR::isError($query)) {
return $query;
}
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$lobs = array();
foreach ((array)$types as $param => $type) {
if (($type == 'clob') || ($type == 'blob')) {
$lobs[$param] = $table_fields[$param];
}
}
return $db->prepare($query, $types, $result_types, $lobs);
}
// }}}
// {{{ autoExecute()
/**
* Generate an insert, update or delete query and call prepare() and execute() on it
*
* @param string name of the table
* @param array assoc ($key=>$value) where $key is a field name and $value its value
* @param int type of query to build
* MDB2_AUTOQUERY_INSERT
* MDB2_AUTOQUERY_UPDATE
* MDB2_AUTOQUERY_DELETE
* MDB2_AUTOQUERY_SELECT
* @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
* @param array that contains the types of the placeholders
* @param string which specifies which result class to use
* @param mixed array that contains the types of the columns in
* the result set or MDB2_PREPARE_RESULT, if set to
* MDB2_PREPARE_MANIP the query is handled as a manipulation query
*
* @return bool|MDB2_Error true on success, a MDB2 error on failure
* @see buildManipSQL
* @see autoPrepare
* @access public
*/
function &autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT,
$where = false, $types = null, $result_class = true, $result_types = MDB2_PREPARE_MANIP)
{
$fields_values = (array)$fields_values;
if ($mode == MDB2_AUTOQUERY_SELECT) {
if (is_array($result_types)) {
$keys = array_keys($result_types);
} elseif (!empty($fields_values)) {
$keys = $fields_values;
} else {
$keys = array();
}
} else {
$keys = array_keys($fields_values);
}
$params = array_values($fields_values);
if (empty($params)) {
$query = $this->buildManipSQL($table, $keys, $mode, $where);
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if ($mode == MDB2_AUTOQUERY_SELECT) {
$result =& $db->query($query, $result_types, $result_class);
} else {
$result = $db->exec($query);
}
} else {
$stmt = $this->autoPrepare($table, $keys, $mode, $where, $types, $result_types);
if (PEAR::isError($stmt)) {
return $stmt;
}
$result =& $stmt->execute($params, $result_class);
$stmt->free();
}
return $result;
}
// }}}
// {{{ buildManipSQL()
/**
* Make automaticaly an sql query for prepare()
*
* Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), MDB2_AUTOQUERY_INSERT)
* will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
* NB : - This belongs more to a SQL Builder class, but this is a simple facility
* - Be carefull ! If you don't give a $where param with an UPDATE/DELETE query, all
* the records of the table will be updated/deleted !
*
* @param string name of the table
* @param ordered array containing the fields names
* @param int type of query to build
* MDB2_AUTOQUERY_INSERT
* MDB2_AUTOQUERY_UPDATE
* MDB2_AUTOQUERY_DELETE
* MDB2_AUTOQUERY_SELECT
* @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
*
* @return string sql query for prepare()
* @access public
*/
function buildManipSQL($table, $table_fields, $mode, $where = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if ($db->options['quote_identifier']) {
$table = $db->quoteIdentifier($table);
}
if (!empty($table_fields) && $db->options['quote_identifier']) {
foreach ($table_fields as $key => $field) {
$table_fields[$key] = $db->quoteIdentifier($field);
}
}
if ($where !== false && !is_null($where)) {
if (is_array($where)) {
$where = implode(' AND ', $where);
}
$where = ' WHERE '.$where;
}
switch ($mode) {
case MDB2_AUTOQUERY_INSERT:
if (empty($table_fields)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Insert requires table fields', __FUNCTION__);
}
$cols = implode(', ', $table_fields);
$values = '?'.str_repeat(', ?', (count($table_fields) - 1));
return 'INSERT INTO '.$table.' ('.$cols.') VALUES ('.$values.')';
break;
case MDB2_AUTOQUERY_UPDATE:
if (empty($table_fields)) {
return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
'Update requires table fields', __FUNCTION__);
}
$set = implode(' = ?, ', $table_fields).' = ?';
$sql = 'UPDATE '.$table.' SET '.$set.$where;
return $sql;
break;
case MDB2_AUTOQUERY_DELETE:
$sql = 'DELETE FROM '.$table.$where;
return $sql;
break;
case MDB2_AUTOQUERY_SELECT:
$cols = !empty($table_fields) ? implode(', ', $table_fields) : '*';
$sql = 'SELECT '.$cols.' FROM '.$table.$where;
return $sql;
break;
}
return $db->raiseError(MDB2_ERROR_SYNTAX, null, null,
'Non existant mode', __FUNCTION__);
}
// }}}
// {{{ limitQuery()
/**
* Generates a limited query
*
* @param string query
* @param array that contains the types of the columns in the result set
* @param integer the numbers of rows to fetch
* @param integer the row to start to fetching
* @param string which specifies which result class to use
* @param mixed string which specifies which class to wrap results in
*
* @return MDB2_Result|MDB2_Error result set on success, a MDB2 error on failure
* @access public
*/
function &limitQuery($query, $types, $limit, $offset = 0, $result_class = true,
$result_wrap_class = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
$result = $db->setLimit($limit, $offset);
if (PEAR::isError($result)) {
return $result;
}
$result =& $db->query($query, $types, $result_class, $result_wrap_class);
return $result;
}
// }}}
// {{{ execParam()
/**
* Execute a parameterized DML statement.
*
* @param string the SQL query
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
*
* @return int|MDB2_Error affected rows on success, a MDB2 error on failure
* @access public
*/
function execParam($query, $params = array(), $param_types = null)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->exec($query);
}
$stmt = $db->prepare($query, $param_types, MDB2_PREPARE_MANIP);
if (PEAR::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (PEAR::isError($result)) {
return $result;
}
$stmt->free();
return $result;
}
// }}}
// {{{ getOne()
/**
* Fetch the first column of the first row of data returned from a query.
* Takes care of doing the query and freeing the results when finished.
*
* @param string the SQL query
* @param string that contains the type of the column in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int|string which column to return
*
* @return scalar|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getOne($query, $type = null, $params = array(),
$param_types = null, $colnum = 0)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
settype($params, 'array');
settype($type, 'array');
if (empty($params)) {
return $db->queryOne($query, $type, $colnum);
}
$stmt = $db->prepare($query, $param_types, $type);
if (PEAR::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$one = $result->fetchOne($colnum);
$stmt->free();
$result->free();
return $one;
}
// }}}
// {{{ getRow()
/**
* Fetch the first row of data returned from a query. Takes care
* of doing the query and freeing the results when finished.
*
* @param string the SQL query
* @param array that contains the types of the columns in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int the fetch mode to use
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getRow($query, $types = null, $params = array(),
$param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->queryRow($query, $types, $fetchmode);
}
$stmt = $db->prepare($query, $param_types, $types);
if (PEAR::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$row = $result->fetchRow($fetchmode);
$stmt->free();
$result->free();
return $row;
}
// }}}
// {{{ getCol()
/**
* Fetch a single column from a result set and return it as an
* indexed array.
*
* @param string the SQL query
* @param string that contains the type of the column in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int|string which column to return
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getCol($query, $type = null, $params = array(),
$param_types = null, $colnum = 0)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
settype($params, 'array');
settype($type, 'array');
if (empty($params)) {
return $db->queryCol($query, $type, $colnum);
}
$stmt = $db->prepare($query, $param_types, $type);
if (PEAR::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$col = $result->fetchCol($colnum);
$stmt->free();
$result->free();
return $col;
}
// }}}
// {{{ getAll()
/**
* Fetch all the rows returned from a query.
*
* @param string the SQL query
* @param array that contains the types of the columns in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param int the fetch mode to use
* @param bool if set to true, the $all will have the first
* column as its first dimension
* @param bool $force_array used only when the query returns exactly
* two columns. If true, the values of the returned array will be
* one-element arrays instead of scalars.
* @param bool $group if true, the values of the returned array is
* wrapped in another array. If the same key value (in the first
* column) repeats itself, the values will be appended to this array
* instead of overwriting the existing values.
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getAll($query, $types = null, $params = array(),
$param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
$rekey = false, $force_array = false, $group = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->queryAll($query, $types, $fetchmode, $rekey, $force_array, $group);
}
$stmt = $db->prepare($query, $param_types, $types);
if (PEAR::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
$stmt->free();
$result->free();
return $all;
}
// }}}
// {{{ getAssoc()
/**
* Fetch the entire result set of a query and return it as an
* associative array using the first column as the key.
*
* If the result set contains more than two columns, the value
* will be an array of the values from column 2-n. If the result
* set contains only two columns, the returned value will be a
* scalar with the value of the second column (unless forced to an
* array with the $force_array parameter). A MDB2 error code is
* returned on errors. If the result set contains fewer than two
* columns, a MDB2_ERROR_TRUNCATED error is returned.
*
* For example, if the table 'mytable' contains:
* <pre>
* ID TEXT DATE
* --------------------------------
* 1 'one' 944679408
* 2 'two' 944679408
* 3 'three' 944679408
* </pre>
* Then the call getAssoc('SELECT id,text FROM mytable') returns:
* <pre>
* array(
* '1' => 'one',
* '2' => 'two',
* '3' => 'three',
* )
* </pre>
* ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
* <pre>
* array(
* '1' => array('one', '944679408'),
* '2' => array('two', '944679408'),
* '3' => array('three', '944679408')
* )
* </pre>
*
* If the more than one row occurs with the same value in the
* first column, the last row overwrites all previous ones by
* default. Use the $group parameter if you don't want to
* overwrite like this. Example:
* <pre>
* getAssoc('SELECT category,id,name FROM mytable', null, null
* MDB2_FETCHMODE_ASSOC, false, true) returns:
* array(
* '1' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* ),
* '9' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* )
* )
* </pre>
*
* Keep in mind that database functions in PHP usually return string
* values for results regardless of the database's internal type.
*
* @param string the SQL query
* @param array that contains the types of the columns in the result set
* @param array if supplied, prepare/execute will be used
* with this array as execute parameters
* @param array that contains the types of the values defined in $params
* @param bool $force_array used only when the query returns
* exactly two columns. If TRUE, the values of the returned array
* will be one-element arrays instead of scalars.
* @param bool $group if TRUE, the values of the returned array
* is wrapped in another array. If the same key value (in the first
* column) repeats itself, the values will be appended to this array
* instead of overwriting the existing values.
*
* @return array|MDB2_Error data on success, a MDB2 error on failure
* @access public
*/
function getAssoc($query, $types = null, $params = array(), $param_types = null,
$fetchmode = MDB2_FETCHMODE_DEFAULT, $force_array = false, $group = false)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
settype($params, 'array');
if (empty($params)) {
return $db->queryAll($query, $types, $fetchmode, true, $force_array, $group);
}
$stmt = $db->prepare($query, $param_types, $types);
if (PEAR::isError($stmt)) {
return $stmt;
}
$result = $stmt->execute($params);
if (!MDB2::isResultCommon($result)) {
return $result;
}
$all = $result->fetchAll($fetchmode, true, $force_array, $group);
$stmt->free();
$result->free();
return $all;
}
// }}}
// {{{ executeMultiple()
/**
* This function does several execute() calls on the same statement handle.
* $params must be an array indexed numerically from 0, one execute call is
* done for every 'row' in the array.
*
* If an error occurs during execute(), executeMultiple() does not execute
* the unfinished rows, but rather returns that error.
*
* @param resource query handle from prepare()
* @param array numeric array containing the data to insert into the query
*
* @return bool|MDB2_Error true on success, a MDB2 error on failure
* @access public
* @see prepare(), execute()
*/
function executeMultiple(&$stmt, $params = null)
{
for ($i = 0, $j = count($params); $i < $j; $i++) {
$result = $stmt->execute($params[$i]);
if (PEAR::isError($result)) {
return $result;
}
}
return MDB2_OK;
}
// }}}
// {{{ getBeforeID()
/**
* Returns the next free id of a sequence if the RDBMS
* does not support auto increment
*
* @param string name of the table into which a new row was inserted
* @param string name of the field into which a new row was inserted
* @param bool when true the sequence is automatic created, if it not exists
* @param bool if the returned value should be quoted
*
* @return int|MDB2_Error id on success, a MDB2 error on failure
* @access public
*/
function getBeforeID($table, $field = null, $ondemand = true, $quote = true)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if ($db->supports('auto_increment') !== true) {
$seq = $table.(empty($field) ? '' : '_'.$field);
$id = $db->nextID($seq, $ondemand);
if (!$quote || PEAR::isError($id)) {
return $id;
}
return $db->quote($id, 'integer');
} elseif (!$quote) {
return null;
}
return 'NULL';
}
// }}}
// {{{ getAfterID()
/**
* Returns the autoincrement ID if supported or $id
*
* @param mixed value as returned by getBeforeId()
* @param string name of the table into which a new row was inserted
* @param string name of the field into which a new row was inserted
*
* @return int|MDB2_Error id on success, a MDB2 error on failure
* @access public
*/
function getAfterID($id, $table, $field = null)
{
$db =& $this->getDBInstance();
if (PEAR::isError($db)) {
return $db;
}
if ($db->supports('auto_increment') !== true) {
return $id;
}
return $db->lastInsertID($table, $field);
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP version 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: Iterator.php,v 1.22 2006/05/06 14:03:41 lsmith Exp $
/**
* PHP5 Iterator
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_Iterator implements Iterator
{
protected $fetchmode;
protected $result;
protected $row;
// {{{ constructor
/**
* Constructor
*/
public function __construct($result, $fetchmode = MDB2_FETCHMODE_DEFAULT)
{
$this->result = $result;
$this->fetchmode = $fetchmode;
}
// }}}
// {{{ seek()
/**
* Seek forward to a specific row in a result set
*
* @param int number of the row where the data can be found
*
* @return void
* @access public
*/
public function seek($rownum)
{
$this->row = null;
if ($this->result) {
$this->result->seek($rownum);
}
}
// }}}
// {{{ next()
/**
* Fetch next row of data
*
* @return void
* @access public
*/
public function next()
{
$this->row = null;
}
// }}}
// {{{ current()
/**
* return a row of data
*
* @return void
* @access public
*/
public function current()
{
if (is_null($this->row)) {
$row = $this->result->fetchRow($this->fetchmode);
if (PEAR::isError($row)) {
$row = false;
}
$this->row = $row;
}
return $this->row;
}
// }}}
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return bool true/false, false is also returned on failure
* @access public
*/
public function valid()
{
return (bool)$this->current();
}
// }}}
// {{{ free()
/**
* Free the internal resources associated with result.
*
* @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
* @access public
*/
public function free()
{
if ($this->result) {
return $this->result->free();
}
$this->result = false;
$this->row = null;
return false;
}
// }}}
// {{{ key()
/**
* Returns the row number
*
* @return int|bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
* @access public
*/
public function key()
{
if ($this->result) {
return $this->result->rowCount();
}
return false;
}
// }}}
// {{{ rewind()
/**
* Seek to the first row in a result set
*
* @return void
* @access public
*/
public function rewind()
{
}
// }}}
// {{{ destructor
/**
* Destructor
*/
public function __destruct()
{
$this->free();
}
// }}}
}
/**
* PHP5 buffered Iterator
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_BufferedIterator extends MDB2_Iterator implements SeekableIterator
{
// {{{ valid()
/**
* Check if the end of the result set has been reached
*
* @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
* @access public
*/
public function valid()
{
if ($this->result) {
return $this->result->valid();
}
return false;
}
// }}}
// {{{count()
/**
* Returns the number of rows in a result object
*
* @return int|MDB2_Error number of rows, false|MDB2_Error if result is invalid
* @access public
*/
public function count()
{
if ($this->result) {
return $this->result->numRows();
}
return false;
}
// }}}
// {{{ rewind()
/**
* Seek to the first row in a result set
*
* @return void
* @access public
*/
public function rewind()
{
$this->seek(0);
}
// }}}
}
?>
<?php
// +----------------------------------------------------------------------+
// | PHP version 5 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
// | Stig. S. Bakken, Lukas Smith |
// | All rights reserved. |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
// | API as well as database abstraction for PHP applications. |
// | This LICENSE is in the BSD license style. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | |
// | Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
// | POSSIBILITY OF SUCH DAMAGE. |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org> |
// +----------------------------------------------------------------------+
//
// $Id: LOB.php,v 1.34 2006/10/25 11:52:21 lsmith Exp $
/**
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
require_once('MDB2.php');
/**
* MDB2_LOB: user land stream wrapper implementation for LOB support
*
* @package MDB2
* @category Database
* @author Lukas Smith <smith@pooteeweet.org>
*/
class MDB2_LOB
{
/**
* contains the key to the global MDB2 instance array of the associated
* MDB2 instance
*
* @var integer
* @access protected
*/
var $db_index;
/**
* contains the key to the global MDB2_LOB instance array of the associated
* MDB2_LOB instance
*
* @var integer
* @access protected
*/
var $lob_index;
// {{{ stream_open()
/**
* open stream
*
* @param string specifies the URL that was passed to fopen()
* @param string the mode used to open the file
* @param int holds additional flags set by the streams API
* @param string not used
*
* @return bool
* @access public
*/
function stream_open($path, $mode, $options, &$opened_path)
{
if (!preg_match('/^rb?\+?$/', $mode)) {
return false;
}
$url = parse_url($path);
if (empty($url['host'])) {
return false;
}
$this->db_index = (int)$url['host'];
if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
return false;
}
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
$this->lob_index = (int)$url['user'];
if (!isset($db->datatype->lobs[$this->lob_index])) {
return false;
}
return true;
}
// }}}
// {{{ stream_read()
/**
* read stream
*
* @param int number of bytes to read
*
* @return string
* @access public
*/
function stream_read($count)
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
$db->datatype->_retrieveLOB($db->datatype->lobs[$this->lob_index]);
$data = $db->datatype->_readLOB($db->datatype->lobs[$this->lob_index], $count);
$length = strlen($data);
if ($length == 0) {
$db->datatype->lobs[$this->lob_index]['endOfLOB'] = true;
}
$db->datatype->lobs[$this->lob_index]['position'] += $length;
return $data;
}
}
// }}}
// {{{ stream_write()
/**
* write stream, note implemented
*
* @param string data
*
* @return int
* @access public
*/
function stream_write($data)
{
return 0;
}
// }}}
// {{{ stream_tell()
/**
* return the current position
*
* @return int current position
* @access public
*/
function stream_tell()
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
return $db->datatype->lobs[$this->lob_index]['position'];
}
}
// }}}
// {{{ stream_eof()
/**
* Check if stream reaches EOF
*
* @return bool
* @access public
*/
function stream_eof()
{
if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
return true;
}
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
$result = $db->datatype->_endOfLOB($db->datatype->lobs[$this->lob_index]);
if (version_compare(phpversion(), "5.0", ">=")
&& version_compare(phpversion(), "5.1", "<")
) {
return !$result;
}
return $result;
}
// }}}
// {{{ stream_seek()
/**
* Seek stream, not implemented
*
* @param int offset
* @param int whence
*
* @return bool
* @access public
*/
function stream_seek($offset, $whence)
{
return false;
}
// }}}
// {{{ stream_stat()
/**
* return information about stream
*
* @access public
*/
function stream_stat()
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
return array(
'db_index' => $this->db_index,
'lob_index' => $this->lob_index,
);
}
}
// }}}
// {{{ stream_close()
/**
* close stream
*
* @access public
*/
function stream_close()
{
if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
$db =& $GLOBALS['_MDB2_databases'][$this->db_index];
if (isset($db->datatype->lobs[$this->lob_index])) {
$db->datatype->_destroyLOB($db->datatype->lobs[$this->lob_index]);
unset($db->datatype->lobs[$this->lob_index]);
}
}
}
// }}}
}
// register streams wrapper
if (!stream_wrapper_register("MDB2LOB", "MDB2_LOB")) {
MDB2::raiseError();
return false;
}
?>
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

This file has been truncated, but you can view the full file.
<?php
/**
* PHP version 4, 5
*
* Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
* Stig. S. Bakken, Lukas Smith, Igor Feghali
* All rights reserved.
*
* MDB2_Schema enables users to maintain RDBMS independant schema files
* in XML that can be used to manipulate both data and database schemas
* This LICENSE is in the BSD license style.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,
* Lukas Smith, Igor Feghali nor the names of his contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Lukas Smith <smith@pooteeweet.org>
* Author: Igor Feghali <ifeghali@php.net>
*
* @category Database
* @package MDB2_Schema
* @author Lukas Smith <smith@pooteeweet.org>
* @author Igor Feghali <ifeghali@php.net>
* @license BSD http://www.opensource.org/licenses/bsd-license.php
* @version CVS: $Id: Schema.php,v 1.132 2009/02/22 21:43:22 ifeghali Exp $
* @link http://pear.php.net/packages/MDB2_Schema
*/
// require_once('MDB2.php');
define('MDB2_SCHEMA_DUMP_ALL', 0);
define('MDB2_SCHEMA_DUMP_STRUCTURE', 1);
define('MDB2_SCHEMA_DUMP_CONTENT', 2);
/**
* If you add an error code here, make sure you also add a textual
* version of it in MDB2_Schema::errorMessage().
*/
define('MDB2_SCHEMA_ERROR', -1);
define('MDB2_SCHEMA_ERROR_PARSE', -2);
define('MDB2_SCHEMA_ERROR_VALIDATE', -3);
define('MDB2_SCHEMA_ERROR_UNSUPPORTED', -4); // Driver does not support this function
define('MDB2_SCHEMA_ERROR_INVALID', -5); // Invalid attribute value
define('MDB2_SCHEMA_ERROR_WRITER', -6);
/**
* The database manager is a class that provides a set of database
* management services like installing, altering and dumping the data
* structures of databases.
*
* @category Database
* @package MDB2_Schema
* @author Lukas Smith <smith@pooteeweet.org>
* @license BSD http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/packages/MDB2_Schema
*/
class MDB2_Schema extends PEAR
{
// {{{ properties
var $db;
var $warnings = array();
var $options = array(
'fail_on_invalid_names' => true,
'dtd_file' => false,
'valid_types' => array(),
'force_defaults' => true,
'parser' => 'MDB2_Schema_Parser',
'writer' => 'MDB2_Schema_Writer',
'validate' => 'MDB2_Schema_Validate',
'drop_missing_tables' => false
);
// }}}
// {{{ apiVersion()
/**
* Return the MDB2 API version
*
* @return string the MDB2 API version number
* @access public
*/
function apiVersion()
{
return '0.4.3';
}
// }}}
// {{{ arrayMergeClobber()
/**
* Clobbers two arrays together
*
* @param array $a1 array that should be clobbered
* @param array $a2 array that should be clobbered
*
* @return array|false array on success and false on error
*
* @access public
* @author kc@hireability.com
*/
function arrayMergeClobber($a1, $a2)
{
if (!is_array($a1) || !is_array($a2)) {
return false;
}
foreach ($a2 as $key => $val) {
if (is_array($val) && array_key_exists($key, $a1) && is_array($a1[$key])) {
$a1[$key] = MDB2_Schema::arrayMergeClobber($a1[$key], $val);
} else {
$a1[$key] = $val;
}
}
return $a1;
}
// }}}
// {{{ resetWarnings()
/**
* reset the warning array
*
* @access public
* @return void
*/
function resetWarnings()
{
$this->warnings = array();
}
// }}}
// {{{ getWarnings()
/**
* Get all warnings in reverse order
*
* This means that the last warning is the first element in the array
*
* @return array with warnings
* @access public
* @see resetWarnings()
*/
function getWarnings()
{
return array_reverse($this->warnings);
}
// }}}
// {{{ setOption()
/**
* Sets the option for the db class
*
* @param string $option option name
* @param mixed $value value for the option
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function setOption($option, $value)
{
if (isset($this->options[$option])) {
if (is_null($value)) {
return $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
'may not set an option to value null');
}
$this->options[$option] = $value;
return MDB2_OK;
}
return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
"unknown option $option");
}
// }}}
// {{{ getOption()
/**
* returns the value of an option
*
* @param string $option option name
*
* @return mixed the option value or error object
* @access public
*/
function getOption($option)
{
if (isset($this->options[$option])) {
return $this->options[$option];
}
return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED,
null, null, "unknown option $option");
}
// }}}
// {{{ factory()
/**
* Create a new MDB2 object for the specified database type
* type
*
* @param string|array|MDB2_Driver_Common &$db 'data source name', see the
* MDB2::parseDSN method for a description of the dsn format.
* Can also be specified as an array of the
* format returned by @see MDB2::parseDSN.
* Finally you can also pass an existing db object to be used.
* @param array $options An associative array of option names and their values.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
* @see MDB2::parseDSN
*/
static function factory(&$db, $options = array())
{
$obj =new MDB2_Schema();
$result = $obj->connect($db, $options);
if (PEAR::isError($result)) {
return $result;
}
return $obj;
}
// }}}
// {{{ connect()
/**
* Create a new MDB2 connection object and connect to the specified
* database
*
* @param string|array|MDB2_Driver_Common &$db 'data source name', see the
* MDB2::parseDSN method for a description of the dsn format.
* Can also be specified as an array of the
* format returned by MDB2::parseDSN.
* Finally you can also pass an existing db object to be used.
* @param array $options An associative array of option names and their values.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
* @see MDB2::parseDSN
*/
function connect(&$db, $options = array())
{
$db_options = array();
if (is_array($options)) {
foreach ($options as $option => $value) {
if (array_key_exists($option, $this->options)) {
$result = $this->setOption($option, $value);
if (PEAR::isError($result)) {
return $result;
}
} else {
$db_options[$option] = $value;
}
}
}
$this->disconnect();
if (!MDB2::isConnection($db)) {
$db =MDB2::factory($db, $db_options);
}
if (PEAR::isError($db)) {
return $db;
}
$this->db =& $db;
$this->db->loadModule('Datatype');
$this->db->loadModule('Manager');
$this->db->loadModule('Reverse');
$this->db->loadModule('Function');
if (empty($this->options['valid_types'])) {
$this->options['valid_types'] = $this->db->datatype->getValidTypes();
}
return MDB2_OK;
}
// }}}
// {{{ disconnect()
/**
* Log out and disconnect from the database.
*
* @access public
* @return void
*/
function disconnect()
{
if (MDB2::isConnection($this->db)) {
$this->db->disconnect();
unset($this->db);
}
}
// }}}
// {{{ parseDatabaseDefinition()
/**
* Parse a database definition from a file or an array
*
* @param string|array $schema the database schema array or file name
* @param bool $skip_unreadable if non readable files should be skipped
* @param array $variables associative array that the defines the text string values
* that are meant to be used to replace the variables that are
* used in the schema description.
* @param bool $fail_on_invalid_names make function fail on invalid names
* @param array $structure database structure definition
*
* @access public
* @return array
*/
function parseDatabaseDefinition($schema, $skip_unreadable = false, $variables = array(),
$fail_on_invalid_names = true, $structure = false)
{
$database_definition = false;
if (is_string($schema)) {
// if $schema is not readable then we just skip it
// and simply copy the $current_schema file to that file name
if (is_readable($schema)) {
$database_definition = $this->parseDatabaseDefinitionFile($schema, $variables, $fail_on_invalid_names, $structure);
}
} elseif (is_array($schema)) {
$database_definition = $schema;
}
if (!$database_definition && !$skip_unreadable) {
$database_definition = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
'invalid data type of schema or unreadable data source');
}
return $database_definition;
}
// }}}
// {{{ parseDatabaseDefinitionFile()
/**
* Parse a database definition file by creating a schema format
* parser object and passing the file contents as parser input data stream.
*
* @param string $input_file the database schema file.
* @param array $variables associative array that the defines the text string values
* that are meant to be used to replace the variables that are
* used in the schema description.
* @param bool $fail_on_invalid_names make function fail on invalid names
* @param array $structure database structure definition
*
* @access public
* @return array
*/
function parseDatabaseDefinitionFile($input_file, $variables = array(),
$fail_on_invalid_names = true, $structure = false)
{
$dtd_file = $this->options['dtd_file'];
if ($dtd_file) {
include_once 'XML/DTD/XmlValidator.php';
$dtd =new XML_DTD_XmlValidator;
if (!$dtd->isValid($dtd_file, $input_file)) {
return $this->raiseError(MDB2_SCHEMA_ERROR_PARSE, null, null, $dtd->getMessage());
}
}
$class_name = $this->options['parser'];
$result = MDB2::loadClass($class_name, $this->db->getOption('debug'));
if (PEAR::isError($result)) {
return $result;
}
$parser =new $class_name($variables, $fail_on_invalid_names, $structure, $this->options['valid_types'], $this->options['force_defaults']);
$result = $parser->setInputFile($input_file);
if (PEAR::isError($result)) {
return $result;
}
$result = $parser->parse();
if (PEAR::isError($result)) {
return $result;
}
if (PEAR::isError($parser->error)) {
return $parser->error;
}
return $parser->database_definition;
}
// }}}
// {{{ getDefinitionFromDatabase()
/**
* Attempt to reverse engineer a schema structure from an existing MDB2
* This method can be used if no xml schema file exists yet.
* The resulting xml schema file may need some manual adjustments.
*
* @return array|MDB2_Error array with definition or error object
* @access public
*/
function getDefinitionFromDatabase()
{
$database = $this->db->database_name;
if (empty($database)) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'it was not specified a valid database name');
}
$class_name = $this->options['validate'];
$result = MDB2::loadClass($class_name, $this->db->getOption('debug'));
if (PEAR::isError($result)) {
return $result;
}
$val =new $class_name($this->options['fail_on_invalid_names'], $this->options['valid_types'], $this->options['force_defaults']);
$database_definition = array(
'name' => $database,
'create' => true,
'overwrite' => false,
'charset' => 'utf8',
'description' => '',
'comments' => '',
'tables' => array(),
'sequences' => array(),
);
$tables = $this->db->manager->listTables();
if (PEAR::isError($tables)) {
return $tables;
}
foreach ($tables as $table_name) {
$fields = $this->db->manager->listTableFields($table_name);
if (PEAR::isError($fields)) {
return $fields;
}
$database_definition['tables'][$table_name] = array(
'was' => '',
'description' => '',
'comments' => '',
'fields' => array(),
'indexes' => array(),
'constraints' => array(),
'initialization' => array()
);
$table_definition =& $database_definition['tables'][$table_name];
foreach ($fields as $field_name) {
$definition = $this->db->reverse->getTableFieldDefinition($table_name, $field_name);
if (PEAR::isError($definition)) {
return $definition;
}
if (!empty($definition[0]['autoincrement'])) {
$definition[0]['default'] = '0';
}
$table_definition['fields'][$field_name] = $definition[0];
$field_choices = count($definition);
if ($field_choices > 1) {
$warning = "There are $field_choices type choices in the table $table_name field $field_name (#1 is the default): ";
$field_choice_cnt = 1;
$table_definition['fields'][$field_name]['choices'] = array();
foreach ($definition as $field_choice) {
$table_definition['fields'][$field_name]['choices'][] = $field_choice;
$warning .= 'choice #'.($field_choice_cnt).': '.serialize($field_choice);
$field_choice_cnt++;
}
$this->warnings[] = $warning;
}
/**
* The first parameter is used to verify if there are duplicated
* fields which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateField(array(), $table_definition['fields'][$field_name], $field_name);
if (PEAR::isError($result)) {
return $result;
}
}
$keys = array();
$indexes = $this->db->manager->listTableIndexes($table_name);
if (PEAR::isError($indexes)) {
return $indexes;
}
if (is_array($indexes)) {
foreach ($indexes as $index_name) {
$this->db->expectError(MDB2_ERROR_NOT_FOUND);
$definition = $this->db->reverse->getTableIndexDefinition($table_name, $index_name);
$this->db->popExpect();
if (PEAR::isError($definition)) {
if (PEAR::isError($definition, MDB2_ERROR_NOT_FOUND)) {
continue;
}
return $definition;
}
$keys[$index_name] = $definition;
}
}
$constraints = $this->db->manager->listTableConstraints($table_name);
if (PEAR::isError($constraints)) {
return $constraints;
}
if (is_array($constraints)) {
foreach ($constraints as $constraint_name) {
$this->db->expectError(MDB2_ERROR_NOT_FOUND);
$definition = $this->db->reverse->getTableConstraintDefinition($table_name, $constraint_name);
$this->db->popExpect();
if (PEAR::isError($definition)) {
if (PEAR::isError($definition, MDB2_ERROR_NOT_FOUND)) {
continue;
}
return $definition;
}
$keys[$constraint_name] = $definition;
}
}
foreach ($keys as $key_name => $definition) {
if (array_key_exists('foreign', $definition)
&& $definition['foreign']
) {
/**
* The first parameter is used to verify if there are duplicated
* foreign keys which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateConstraint(array(), $definition, $key_name);
if (PEAR::isError($result)) {
return $result;
}
foreach ($definition['fields'] as $field_name => $field) {
/**
* The first parameter is used to verify if there are duplicated
* referencing fields which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateConstraintField(array(), $field_name);
if (PEAR::isError($result)) {
return $result;
}
$definition['fields'][$field_name] = '';
}
foreach ($definition['references']['fields'] as $field_name => $field) {
/**
* The first parameter is used to verify if there are duplicated
* referenced fields which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateConstraintReferencedField(array(), $field_name);
if (PEAR::isError($result)) {
return $result;
}
$definition['references']['fields'][$field_name] = '';
}
$table_definition['constraints'][$key_name] = $definition;
} else {
/**
* The first parameter is used to verify if there are duplicated
* indices which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateIndex(array(), $definition, $key_name);
if (PEAR::isError($result)) {
return $result;
}
foreach ($definition['fields'] as $field_name => $field) {
/**
* The first parameter is used to verify if there are duplicated
* index fields which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateIndexField(array(), $field, $field_name);
if (PEAR::isError($result)) {
return $result;
}
$definition['fields'][$field_name] = $field;
}
$table_definition['indexes'][$key_name] = $definition;
}
}
/**
* The first parameter is used to verify if there are duplicated
* tables which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateTable(array(), $table_definition, $table_name);
if (PEAR::isError($result)) {
return $result;
}
}
$sequences = $this->db->manager->listSequences();
if (PEAR::isError($sequences)) {
return $sequences;
}
if (is_array($sequences)) {
foreach ($sequences as $sequence_name) {
$definition = $this->db->reverse->getSequenceDefinition($sequence_name);
if (PEAR::isError($definition)) {
return $definition;
}
if (isset($database_definition['tables'][$sequence_name])
&& isset($database_definition['tables'][$sequence_name]['indexes'])
) {
foreach ($database_definition['tables'][$sequence_name]['indexes'] as $index) {
if (isset($index['primary']) && $index['primary']
&& count($index['fields'] == 1)
) {
$definition['on'] = array(
'table' => $sequence_name,
'field' => key($index['fields']),
);
break;
}
}
}
/**
* The first parameter is used to verify if there are duplicated
* sequences which we can guarantee that won't happen when reverse engineering
*/
$result = $val->validateSequence(array(), $definition, $sequence_name);
if (PEAR::isError($result)) {
return $result;
}
$database_definition['sequences'][$sequence_name] = $definition;
}
}
$result = $val->validateDatabase($database_definition);
if (PEAR::isError($result)) {
return $result;
}
return $database_definition;
}
// }}}
// {{{ createTableIndexes()
/**
* A method to create indexes for an existing table
*
* @param string $table_name Name of the table
* @param array $indexes An array of indexes to be created
* @param boolean $overwrite If the table/index should be overwritten if it already exists
*
* @return mixed MDB2_Error if there is an error creating an index, MDB2_OK otherwise
* @access public
*/
function createTableIndexes($table_name, $indexes, $overwrite = false)
{
if (!$this->db->supports('indexes')) {
$this->db->debug('Indexes are not supported', __FUNCTION__);
return MDB2_OK;
}
$errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
foreach ($indexes as $index_name => $index) {
// Does the index already exist, and if so, should it be overwritten?
$create_index = true;
$this->db->expectError($errorcodes);
if (!empty($index['primary']) || !empty($index['unique'])) {
$current_indexes = $this->db->manager->listTableConstraints($table_name);
} else {
$current_indexes = $this->db->manager->listTableIndexes($table_name);
}
$this->db->popExpect();
if (PEAR::isError($current_indexes)) {
if (!MDB2::isError($current_indexes, $errorcodes)) {
return $current_indexes;
}
} elseif (is_array($current_indexes) && in_array($index_name, $current_indexes)) {
if (!$overwrite) {
$this->db->debug('Index already exists: '.$index_name, __FUNCTION__);
$create_index = false;
} else {
$this->db->debug('Preparing to overwrite index: '.$index_name, __FUNCTION__);
$this->db->expectError(MDB2_ERROR_NOT_FOUND);
if (!empty($index['primary']) || !empty($index['unique'])) {
$result = $this->db->manager->dropConstraint($table_name, $index_name);
} else {
$result = $this->db->manager->dropIndex($table_name, $index_name);
}
$this->db->popExpect();
if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_NOT_FOUND)) {
return $result;
}
}
}
// Check if primary is being used and if it's supported
if (!empty($index['primary']) && !$this->db->supports('primary_key')) {
// Primary not supported so we fallback to UNIQUE and making the field NOT NULL
$index['unique'] = true;
$changes = array();
foreach ($index['fields'] as $field => $empty) {
$field_info = $this->db->reverse->getTableFieldDefinition($table_name, $field);
if (PEAR::isError($field_info)) {
return $field_info;
}
if (!$field_info[0]['notnull']) {
$changes['change'][$field] = $field_info[0];
$changes['change'][$field]['notnull'] = true;
}
}
if (!empty($changes)) {
$this->db->manager->alterTable($table_name, $changes, false);
}
}
// Should the index be created?
if ($create_index) {
if (!empty($index['primary']) || !empty($index['unique'])) {
$result = $this->db->manager->createConstraint($table_name, $index_name, $index);
} else {
$result = $this->db->manager->createIndex($table_name, $index_name, $index);
}
if (PEAR::isError($result)) {
return $result;
}
}
}
return MDB2_OK;
}
// }}}
// {{{ createTableConstraints()
/**
* A method to create foreign keys for an existing table
*
* @param string $table_name Name of the table
* @param array $constraints An array of foreign keys to be created
* @param boolean $overwrite If the foreign key should be overwritten if it already exists
*
* @return mixed MDB2_Error if there is an error creating a foreign key, MDB2_OK otherwise
* @access public
*/
function createTableConstraints($table_name, $constraints, $overwrite = false)
{
if (!$this->db->supports('indexes')) {
$this->db->debug('Indexes are not supported', __FUNCTION__);
return MDB2_OK;
}
$errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
foreach ($constraints as $constraint_name => $constraint) {
// Does the foreign key already exist, and if so, should it be overwritten?
$create_constraint = true;
$this->db->expectError($errorcodes);
$current_constraints = $this->db->manager->listTableConstraints($table_name);
$this->db->popExpect();
if (PEAR::isError($current_constraints)) {
if (!MDB2::isError($current_constraints, $errorcodes)) {
return $current_constraints;
}
} elseif (is_array($current_constraints) && in_array($constraint_name, $current_constraints)) {
if (!$overwrite) {
$this->db->debug('Foreign key already exists: '.$constraint_name, __FUNCTION__);
$create_constraint = false;
} else {
$this->db->debug('Preparing to overwrite foreign key: '.$constraint_name, __FUNCTION__);
$result = $this->db->manager->dropConstraint($table_name, $constraint_name);
if (PEAR::isError($result)) {
return $result;
}
}
}
// Should the foreign key be created?
if ($create_constraint) {
$result = $this->db->manager->createConstraint($table_name, $constraint_name, $constraint);
if (PEAR::isError($result)) {
return $result;
}
}
}
return MDB2_OK;
}
// }}}
// {{{ createTable()
/**
* Create a table and inititialize the table if data is available
*
* @param string $table_name name of the table to be created
* @param array $table multi dimensional array that contains the
* structure and optional data of the table
* @param bool $overwrite if the table/index should be overwritten if it already exists
* @param array $options an array of options to be passed to the database specific driver
* version of MDB2_Driver_Manager_Common::createTable().
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function createTable($table_name, $table, $overwrite = false, $options = array())
{
$create = true;
$errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
$this->db->expectError($errorcodes);
$tables = $this->db->manager->listTables();
$this->db->popExpect();
if (PEAR::isError($tables)) {
if (!MDB2::isError($tables, $errorcodes)) {
return $tables;
}
} elseif (is_array($tables) && in_array($table_name, $tables)) {
if (!$overwrite) {
$create = false;
$this->db->debug('Table already exists: '.$table_name, __FUNCTION__);
} else {
$result = $this->db->manager->dropTable($table_name);
if (PEAR::isError($result)) {
return $result;
}
$this->db->debug('Overwritting table: '.$table_name, __FUNCTION__);
}
}
if ($create) {
$result = $this->db->manager->createTable($table_name, $table['fields'], $options);
if (PEAR::isError($result)) {
return $result;
}
}
if (!empty($table['initialization']) && is_array($table['initialization'])) {
$result = $this->initializeTable($table_name, $table);
if (PEAR::isError($result)) {
return $result;
}
}
if (!empty($table['indexes']) && is_array($table['indexes'])) {
$result = $this->createTableIndexes($table_name, $table['indexes'], $overwrite);
if (PEAR::isError($result)) {
return $result;
}
}
if (!empty($table['constraints']) && is_array($table['constraints'])) {
$result = $this->createTableConstraints($table_name, $table['constraints'], $overwrite);
if (PEAR::isError($result)) {
return $result;
}
}
return MDB2_OK;
}
// }}}
// {{{ initializeTable()
/**
* Inititialize the table with data
*
* @param string $table_name name of the table
* @param array $table multi dimensional array that contains the
* structure and optional data of the table
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function initializeTable($table_name, $table)
{
$query_insertselect = 'INSERT INTO %s (%s) (SELECT %s FROM %s %s)';
$query_insert = 'INSERT INTO %s (%s) VALUES (%s)';
$query_update = 'UPDATE %s SET %s %s';
$query_delete = 'DELETE FROM %s %s';
$table_name = $this->db->quoteIdentifier($table_name, true);
$result = MDB2_OK;
$support_transactions = $this->db->supports('transactions');
foreach ($table['initialization'] as $instruction) {
$query = '';
switch ($instruction['type']) {
case 'insert':
if (!isset($instruction['data']['select'])) {
$data = $this->getInstructionFields($instruction['data'], $table['fields']);
if (!empty($data)) {
$fields = implode(', ', array_keys($data));
$values = implode(', ', array_values($data));
$query = sprintf($query_insert, $table_name, $fields, $values);
}
} else {
$data = $this->getInstructionFields($instruction['data']['select'], $table['fields']);
$where = $this->getInstructionWhere($instruction['data']['select'], $table['fields']);
$select_table_name = $this->db->quoteIdentifier($instruction['data']['select']['table'], true);
if (!empty($data)) {
$fields = implode(', ', array_keys($data));
$values = implode(', ', array_values($data));
$query = sprintf($query_insertselect, $table_name, $fields, $values, $select_table_name, $where);
}
}
break;
case 'update':
$data = $this->getInstructionFields($instruction['data'], $table['fields']);
$where = $this->getInstructionWhere($instruction['data'], $table['fields']);
if (!empty($data)) {
array_walk($data, array($this, 'buildFieldValue'));
$fields_values = implode(', ', $data);
$query = sprintf($query_update, $table_name, $fields_values, $where);
}
break;
case 'delete':
$where = $this->getInstructionWhere($instruction['data'], $table['fields']);
$query = sprintf($query_delete, $table_name, $where);
break;
}
if ($query) {
if ($support_transactions && PEAR::isError($res = $this->db->beginNestedTransaction())) {
return $res;
}
$result = $this->db->exec($query);
if (PEAR::isError($result)) {
return $result;
}
if ($support_transactions && PEAR::isError($res = $this->db->completeNestedTransaction())) {
return $res;
}
}
}
return $result;
}
// }}}
// {{{ buildFieldValue()
/**
* Appends the contents of second argument + '=' to the beginning of first
* argument.
*
* Used with array_walk() in initializeTable() for UPDATEs.
*
* @param string &$element value of array's element
* @param string $key key of array's element
*
* @return void
*
* @access public
* @see MDB2_Schema::initializeTable()
*/
function buildFieldValue(&$element, $key)
{
$element = $key."=$element";
}
// }}}
// {{{ getExpression()
/**
* Generates a string that represents a value that would be associated
* with a column in a DML instruction.
*
* @param array $element multi dimensional array that contains the
* structure of the current DML instruction.
* @param array $fields_definition multi dimensional array that contains the
* definition for current table's fields
* @param string $type type of given field
*
* @return string
*
* @access public
* @see MDB2_Schema::getInstructionFields(), MDB2_Schema::getInstructionWhere()
*/
function getExpression($element, $fields_definition = array(), $type = null)
{
$str = '';
switch ($element['type']) {
case 'null':
$str .= 'NULL';
break;
case 'value':
$str .= $this->db->quote($element['data'], $type);
break;
case 'column':
$str .= $this->db->quoteIdentifier($element['data'], true);
break;
case 'function':
$arguments = array();
if (!empty($element['data']['arguments'])
&& is_array($element['data']['arguments'])
) {
foreach ($element['data']['arguments'] as $v) {
$arguments[] = $this->getExpression($v, $fields_definition);
}
}
if (method_exists($this->db->function, $element['data']['name'])) {
$user_func = array(&$this->db->function, $element['data']['name']);
$str .= call_user_func_array($user_func, $arguments);
} else {
$str .= $element['data']['name'].'(';
$str .= implode(', ', $arguments);
$str .= ')';
}
break;
case 'expression':
$type0 = $type1 = null;
if ($element['data']['operants'][0]['type'] == 'column'
&& array_key_exists($element['data']['operants'][0]['data'], $fields_definition)
) {
$type0 = $fields_definition[$element['data']['operants'][0]['data']]['type'];
}
if ($element['data']['operants'][1]['type'] == 'column'
&& array_key_exists($element['data']['operants'][1]['data'], $fields_definition)
) {
$type1 = $fields_definition[$element['data']['operants'][1]['data']]['type'];
}
$str .= '(';
$str .= $this->getExpression($element['data']['operants'][0], $fields_definition, $type1);
$str .= $this->getOperator($element['data']['operator']);
$str .= $this->getExpression($element['data']['operants'][1], $fields_definition, $type0);
$str .= ')';
break;
}
return $str;
}
// }}}
// {{{ getOperator()
/**
* Returns the matching SQL operator
*
* @param string $op parsed descriptive operator
*
* @return string matching SQL operator
*
* @access public
* @static
* @see MDB2_Schema::getExpression()
*/
function getOperator($op)
{
switch ($op) {
case 'PLUS':
return ' + ';
case 'MINUS':
return ' - ';
case 'TIMES':
return ' * ';
case 'DIVIDED':
return ' / ';
case 'EQUAL':
return ' = ';
case 'NOT EQUAL':
return ' != ';
case 'LESS THAN':
return ' < ';
case 'GREATER THAN':
return ' > ';
case 'LESS THAN OR EQUAL':
return ' <= ';
case 'GREATER THAN OR EQUAL':
return ' >= ';
default:
return ' '.$op.' ';
}
}
// }}}
// {{{ getInstructionFields()
/**
* Walks the parsed DML instruction array, field by field,
* storing them and their processed values inside a new array.
*
* @param array $instruction multi dimensional array that contains the
* structure of the current DML instruction.
* @param array $fields_definition multi dimensional array that contains the
* definition for current table's fields
*
* @return array array of strings in the form 'field_name' => 'value'
*
* @access public
* @static
* @see MDB2_Schema::initializeTable()
*/
function getInstructionFields($instruction, $fields_definition = array())
{
$fields = array();
if (!empty($instruction['field']) && is_array($instruction['field'])) {
foreach ($instruction['field'] as $field) {
$field_name = $this->db->quoteIdentifier($field['name'], true);
$fields[$field_name] = $this->getExpression($field['group'], $fields_definition);
}
}
return $fields;
}
// }}}
// {{{ getInstructionWhere()
/**
* Translates the parsed WHERE expression of a DML instruction
* (array structure) to a SQL WHERE clause (string).
*
* @param array $instruction multi dimensional array that contains the
* structure of the current DML instruction.
* @param array $fields_definition multi dimensional array that contains the
* definition for current table's fields.
*
* @return string SQL WHERE clause
*
* @access public
* @static
* @see MDB2_Schema::initializeTable()
*/
function getInstructionWhere($instruction, $fields_definition = array())
{
$where = '';
if (!empty($instruction['where'])) {
$where = 'WHERE '.$this->getExpression($instruction['where'], $fields_definition);
}
return $where;
}
// }}}
// {{{ createSequence()
/**
* Create a sequence
*
* @param string $sequence_name name of the sequence to be created
* @param array $sequence multi dimensional array that contains the
* structure and optional data of the table
* @param bool $overwrite if the sequence should be overwritten if it already exists
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function createSequence($sequence_name, $sequence, $overwrite = false)
{
if (!$this->db->supports('sequences')) {
$this->db->debug('Sequences are not supported', __FUNCTION__);
return MDB2_OK;
}
$errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
$this->db->expectError($errorcodes);
$sequences = $this->db->manager->listSequences();
$this->db->popExpect();
if (PEAR::isError($sequences)) {
if (!MDB2::isError($sequences, $errorcodes)) {
return $sequences;
}
} elseif (is_array($sequence) && in_array($sequence_name, $sequences)) {
if (!$overwrite) {
$this->db->debug('Sequence already exists: '.$sequence_name, __FUNCTION__);
return MDB2_OK;
}
$result = $this->db->manager->dropSequence($sequence_name);
if (PEAR::isError($result)) {
return $result;
}
$this->db->debug('Overwritting sequence: '.$sequence_name, __FUNCTION__);
}
$start = 1;
$field = '';
if (!empty($sequence['on'])) {
$table = $sequence['on']['table'];
$field = $sequence['on']['field'];
$errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
$this->db->expectError($errorcodes);
$tables = $this->db->manager->listTables();
$this->db->popExpect();
if (PEAR::isError($tables) && !MDB2::isError($tables, $errorcodes)) {
return $tables;
}
if (!PEAR::isError($tables) && is_array($tables) && in_array($table, $tables)) {
if ($this->db->supports('summary_functions')) {
$query = "SELECT MAX($field) FROM ".$this->db->quoteIdentifier($table, true);
} else {
$query = "SELECT $field FROM ".$this->db->quoteIdentifier($table, true)." ORDER BY $field DESC";
}
$start = $this->db->queryOne($query, 'integer');
if (PEAR::isError($start)) {
return $start;
}
++$start;
} else {
$this->warnings[] = 'Could not sync sequence: '.$sequence_name;
}
} elseif (!empty($sequence['start']) && is_numeric($sequence['start'])) {
$start = $sequence['start'];
$table = '';
}
$result = $this->db->manager->createSequence($sequence_name, $start);
if (PEAR::isError($result)) {
return $result;
}
return MDB2_OK;
}
// }}}
// {{{ createDatabase()
/**
* Create a database space within which may be created database objects
* like tables, indexes and sequences. The implementation of this function
* is highly DBMS specific and may require special permissions to run
* successfully. Consult the documentation or the DBMS drivers that you
* use to be aware of eventual configuration requirements.
*
* @param array $database_definition multi dimensional array that contains the current definition
* @param array $options an array of options to be passed to the
* database specific driver version of
* MDB2_Driver_Manager_Common::createTable().
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function createDatabase($database_definition, $options = array())
{
if (!isset($database_definition['name']) || !$database_definition['name']) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'no valid database name specified');
}
$create = (isset($database_definition['create']) && $database_definition['create']);
$overwrite = (isset($database_definition['overwrite']) && $database_definition['overwrite']);
/**
*
* We need to clean up database name before any query to prevent
* database driver from using a inexistent database
*
*/
$previous_database_name = $this->db->setDatabase('');
// Lower / Upper case the db name if the portability deems so.
if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
$func = $this->db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper';
$db_name = $func($database_definition['name']);
} else {
$db_name = $database_definition['name'];
}
if ($create) {
$dbExists = $this->db->databaseExists($db_name);
if (PEAR::isError($dbExists)) {
return $dbExists;
}
if ($dbExists && $overwrite) {
$this->db->expectError(MDB2_ERROR_CANNOT_DROP);
$result = $this->db->manager->dropDatabase($db_name);
$this->db->popExpect();
if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_CANNOT_DROP)) {
return $result;
}
$dbExists = false;
$this->db->debug('Overwritting database: ' . $db_name, __FUNCTION__);
}
$dbOptions = array();
if (array_key_exists('charset', $database_definition)
&& !empty($database_definition['charset'])) {
$dbOptions['charset'] = $database_definition['charset'];
}
if ($dbExists) {
$this->db->debug('Database already exists: ' . $db_name, __FUNCTION__);
// if (!empty($dbOptions)) {
// $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NO_PERMISSION);
// $this->db->expectError($errorcodes);
// $result = $this->db->manager->alterDatabase($db_name, $dbOptions);
// $this->db->popExpect();
// if (PEAR::isError($result) && !MDB2::isError($result, $errorcodes)) {
// return $result;
// }
// }
$create = false;
} else {
$this->db->expectError(MDB2_ERROR_UNSUPPORTED);
$result = $this->db->manager->createDatabase($db_name, $dbOptions);
$this->db->popExpect();
if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_UNSUPPORTED)) {
return $result;
}
$this->db->debug('Creating database: ' . $db_name, __FUNCTION__);
}
}
$this->db->setDatabase($db_name);
if (($support_transactions = $this->db->supports('transactions'))
&& PEAR::isError($result = $this->db->beginNestedTransaction())
) {
return $result;
}
$created_objects = 0;
if (isset($database_definition['tables'])
&& is_array($database_definition['tables'])
) {
foreach ($database_definition['tables'] as $table_name => $table) {
$result = $this->createTable($table_name, $table, $overwrite, $options);
if (PEAR::isError($result)) {
break;
}
$created_objects++;
}
}
if (!PEAR::isError($result)
&& isset($database_definition['sequences'])
&& is_array($database_definition['sequences'])
) {
foreach ($database_definition['sequences'] as $sequence_name => $sequence) {
$result = $this->createSequence($sequence_name, $sequence, false, $overwrite);
if (PEAR::isError($result)) {
break;
}
$created_objects++;
}
}
if ($support_transactions) {
$res = $this->db->completeNestedTransaction();
if (PEAR::isError($res)) {
$result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
'Could not end transaction ('.
$res->getMessage().' ('.$res->getUserinfo().'))');
}
} elseif (PEAR::isError($result) && $created_objects) {
$result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
'the database was only partially created ('.
$result->getMessage().' ('.$result->getUserinfo().'))');
}
$this->db->setDatabase($previous_database_name);
if (PEAR::isError($result) && $create
&& PEAR::isError($result2 = $this->db->manager->dropDatabase($db_name))
) {
if (!MDB2::isError($result2, MDB2_ERROR_UNSUPPORTED)) {
return $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
'Could not drop the created database after unsuccessful creation attempt ('.
$result2->getMessage().' ('.$result2->getUserinfo().'))');
}
}
return $result;
}
// }}}
// {{{ compareDefinitions()
/**
* Compare a previous definition with the currently parsed definition
*
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
*
* @return array|MDB2_Error array of changes on success, or a error object
* @access public
*/
function compareDefinitions($current_definition, $previous_definition)
{
$changes = array();
if (!empty($current_definition['tables']) && is_array($current_definition['tables'])) {
$changes['tables'] = $defined_tables = array();
foreach ($current_definition['tables'] as $table_name => $table) {
$previous_tables = array();
if (!empty($previous_definition) && is_array($previous_definition)) {
$previous_tables = $previous_definition['tables'];
}
$change = $this->compareTableDefinitions($table_name, $table, $previous_tables, $defined_tables);
if (PEAR::isError($change)) {
return $change;
}
if (!empty($change)) {
$changes['tables'] = MDB2_Schema::arrayMergeClobber($changes['tables'], $change);
}
}
if (!empty($previous_definition['tables'])
&& is_array($previous_definition['tables'])) {
foreach ($previous_definition['tables'] as $table_name => $table) {
if (empty($defined_tables[$table_name])) {
$changes['tables']['remove'][$table_name] = true;
}
}
}
}
if (!empty($current_definition['sequences']) && is_array($current_definition['sequences'])) {
$changes['sequences'] = $defined_sequences = array();
foreach ($current_definition['sequences'] as $sequence_name => $sequence) {
$previous_sequences = array();
if (!empty($previous_definition) && is_array($previous_definition)) {
$previous_sequences = $previous_definition['sequences'];
}
$change = $this->compareSequenceDefinitions($sequence_name,
$sequence,
$previous_sequences,
$defined_sequences);
if (PEAR::isError($change)) {
return $change;
}
if (!empty($change)) {
$changes['sequences'] = MDB2_Schema::arrayMergeClobber($changes['sequences'], $change);
}
}
if (!empty($previous_definition['sequences']) && is_array($previous_definition['sequences'])) {
foreach ($previous_definition['sequences'] as $sequence_name => $sequence) {
if (empty($defined_sequences[$sequence_name])) {
$changes['sequences']['remove'][$sequence_name] = true;
}
}
}
}
return $changes;
}
// }}}
// {{{ compareTableFieldsDefinitions()
/**
* Compare a previous definition with the currently parsed definition
*
* @param string $table_name name of the table
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
*
* @return array|MDB2_Error array of changes on success, or a error object
* @access public
*/
function compareTableFieldsDefinitions($table_name, $current_definition,
$previous_definition)
{
$changes = $defined_fields = array();
if (is_array($current_definition)) {
foreach ($current_definition as $field_name => $field) {
$was_field_name = $field['was'];
if (!empty($previous_definition[$field_name])
&& (
(isset($previous_definition[$field_name]['was'])
&& $previous_definition[$field_name]['was'] == $was_field_name)
|| !isset($previous_definition[$was_field_name])
)) {
$was_field_name = $field_name;
}
if (!empty($previous_definition[$was_field_name])) {
if ($was_field_name != $field_name) {
$changes['rename'][$was_field_name] = array('name' => $field_name, 'definition' => $field);
}
if (!empty($defined_fields[$was_field_name])) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'the field "'.$was_field_name.
'" was specified for more than one field of table');
}
$defined_fields[$was_field_name] = true;
$change = $this->db->compareDefinition($field, $previous_definition[$was_field_name]);
if (PEAR::isError($change)) {
return $change;
}
if (!empty($change)) {
if (array_key_exists('default', $change)
&& $change['default']
&& !array_key_exists('default', $field)) {
$field['default'] = null;
}
$change['definition'] = $field;
$changes['change'][$field_name] = $change;
}
} else {
if ($field_name != $was_field_name) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'it was specified a previous field name ("'.
$was_field_name.'") for field "'.$field_name.'" of table "'.
$table_name.'" that does not exist');
}
$changes['add'][$field_name] = $field;
}
}
}
if (isset($previous_definition) && is_array($previous_definition)) {
foreach ($previous_definition as $field_previous_name => $field_previous) {
if (empty($defined_fields[$field_previous_name])) {
$changes['remove'][$field_previous_name] = true;
}
}
}
return $changes;
}
// }}}
// {{{ compareTableIndexesDefinitions()
/**
* Compare a previous definition with the currently parsed definition
*
* @param string $table_name name of the table
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
*
* @return array|MDB2_Error array of changes on success, or a error object
* @access public
*/
function compareTableIndexesDefinitions($table_name, $current_definition,
$previous_definition)
{
$changes = $defined_indexes = array();
if (is_array($current_definition)) {
foreach ($current_definition as $index_name => $index) {
$was_index_name = $index['was'];
if (!empty($previous_definition[$index_name])
&& isset($previous_definition[$index_name]['was'])
&& $previous_definition[$index_name]['was'] == $was_index_name
) {
$was_index_name = $index_name;
}
if (!empty($previous_definition[$was_index_name])) {
$change = array();
if ($was_index_name != $index_name) {
$change['name'] = $was_index_name;
}
if (!empty($defined_indexes[$was_index_name])) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'the index "'.$was_index_name.'" was specified for'.
' more than one index of table "'.$table_name.'"');
}
$defined_indexes[$was_index_name] = true;
$previous_unique = array_key_exists('unique', $previous_definition[$was_index_name])
? $previous_definition[$was_index_name]['unique'] : false;
$unique = array_key_exists('unique', $index) ? $index['unique'] : false;
if ($previous_unique != $unique) {
$change['unique'] = $unique;
}
$previous_primary = array_key_exists('primary', $previous_definition[$was_index_name])
? $previous_definition[$was_index_name]['primary'] : false;
$primary = array_key_exists('primary', $index) ? $index['primary'] : false;
if ($previous_primary != $primary) {
$change['primary'] = $primary;
}
$defined_fields = array();
$previous_fields = $previous_definition[$was_index_name]['fields'];
if (!empty($index['fields']) && is_array($index['fields'])) {
foreach ($index['fields'] as $field_name => $field) {
if (!empty($previous_fields[$field_name])) {
$defined_fields[$field_name] = true;
$previous_sorting = array_key_exists('sorting', $previous_fields[$field_name])
? $previous_fields[$field_name]['sorting'] : '';
$sorting = array_key_exists('sorting', $field) ? $field['sorting'] : '';
if ($previous_sorting != $sorting) {
$change['change'] = true;
}
} else {
$change['change'] = true;
}
}
}
if (isset($previous_fields) && is_array($previous_fields)) {
foreach ($previous_fields as $field_name => $field) {
if (empty($defined_fields[$field_name])) {
$change['change'] = true;
}
}
}
if (!empty($change)) {
$changes['change'][$index_name] = $current_definition[$index_name];
}
} else {
if ($index_name != $was_index_name) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'it was specified a previous index name ("'.$was_index_name.
') for index "'.$index_name.'" of table "'.$table_name.'" that does not exist');
}
$changes['add'][$index_name] = $current_definition[$index_name];
}
}
}
foreach ($previous_definition as $index_previous_name => $index_previous) {
if (empty($defined_indexes[$index_previous_name])) {
$changes['remove'][$index_previous_name] = $index_previous;
}
}
return $changes;
}
// }}}
// {{{ compareTableDefinitions()
/**
* Compare a previous definition with the currently parsed definition
*
* @param string $table_name name of the table
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
* @param array &$defined_tables table names in the schema
*
* @return array|MDB2_Error array of changes on success, or a error object
* @access public
*/
function compareTableDefinitions($table_name, $current_definition,
$previous_definition, &$defined_tables)
{
$changes = array();
if (is_array($current_definition)) {
$was_table_name = $table_name;
if (!empty($current_definition['was'])) {
$was_table_name = $current_definition['was'];
}
if (!empty($previous_definition[$was_table_name])) {
$changes['change'][$was_table_name] = array();
if ($was_table_name != $table_name) {
$changes['change'][$was_table_name] = array('name' => $table_name);
}
if (!empty($defined_tables[$was_table_name])) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'the table "'.$was_table_name.
'" was specified for more than one table of the database');
}
$defined_tables[$was_table_name] = true;
if (!empty($current_definition['fields']) && is_array($current_definition['fields'])) {
$previous_fields = array();
if (isset($previous_definition[$was_table_name]['fields'])
&& is_array($previous_definition[$was_table_name]['fields'])) {
$previous_fields = $previous_definition[$was_table_name]['fields'];
}
$change = $this->compareTableFieldsDefinitions($table_name,
$current_definition['fields'],
$previous_fields);
if (PEAR::isError($change)) {
return $change;
}
if (!empty($change)) {
$changes['change'][$was_table_name] =
MDB2_Schema::arrayMergeClobber($changes['change'][$was_table_name], $change);
}
}
if (!empty($current_definition['indexes']) && is_array($current_definition['indexes'])) {
$previous_indexes = array();
if (isset($previous_definition[$was_table_name]['indexes'])
&& is_array($previous_definition[$was_table_name]['indexes'])) {
$previous_indexes = $previous_definition[$was_table_name]['indexes'];
}
$change = $this->compareTableIndexesDefinitions($table_name,
$current_definition['indexes'],
$previous_indexes);
if (PEAR::isError($change)) {
return $change;
}
if (!empty($change)) {
$changes['change'][$was_table_name]['indexes'] = $change;
}
}
if (empty($changes['change'][$was_table_name])) {
unset($changes['change'][$was_table_name]);
}
if (empty($changes['change'])) {
unset($changes['change']);
}
} else {
if ($table_name != $was_table_name) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'it was specified a previous table name ("'.$was_table_name.
'") for table "'.$table_name.'" that does not exist');
}
$changes['add'][$table_name] = true;
}
}
return $changes;
}
// }}}
// {{{ compareSequenceDefinitions()
/**
* Compare a previous definition with the currently parsed definition
*
* @param string $sequence_name name of the sequence
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
* @param array &$defined_sequences names in the schema
*
* @return array|MDB2_Error array of changes on success, or a error object
* @access public
*/
function compareSequenceDefinitions($sequence_name, $current_definition,
$previous_definition, &$defined_sequences)
{
$changes = array();
if (is_array($current_definition)) {
$was_sequence_name = $sequence_name;
if (!empty($previous_definition[$sequence_name])
&& isset($previous_definition[$sequence_name]['was'])
&& $previous_definition[$sequence_name]['was'] == $was_sequence_name
) {
$was_sequence_name = $sequence_name;
} elseif (!empty($current_definition['was'])) {
$was_sequence_name = $current_definition['was'];
}
if (!empty($previous_definition[$was_sequence_name])) {
if ($was_sequence_name != $sequence_name) {
$changes['change'][$was_sequence_name]['name'] = $sequence_name;
}
if (!empty($defined_sequences[$was_sequence_name])) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'the sequence "'.$was_sequence_name.'" was specified as base'.
' of more than of sequence of the database');
}
$defined_sequences[$was_sequence_name] = true;
$change = array();
if (!empty($current_definition['start'])
&& isset($previous_definition[$was_sequence_name]['start'])
&& $current_definition['start'] != $previous_definition[$was_sequence_name]['start']
) {
$change['start'] = $previous_definition[$sequence_name]['start'];
}
if (isset($current_definition['on']['table'])
&& isset($previous_definition[$was_sequence_name]['on']['table'])
&& $current_definition['on']['table'] != $previous_definition[$was_sequence_name]['on']['table']
&& isset($current_definition['on']['field'])
&& isset($previous_definition[$was_sequence_name]['on']['field'])
&& $current_definition['on']['field'] != $previous_definition[$was_sequence_name]['on']['field']
) {
$change['on'] = $current_definition['on'];
}
if (!empty($change)) {
$changes['change'][$was_sequence_name][$sequence_name] = $change;
}
} else {
if ($sequence_name != $was_sequence_name) {
return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
'it was specified a previous sequence name ("'.$was_sequence_name.
'") for sequence "'.$sequence_name.'" that does not exist');
}
$changes['add'][$sequence_name] = true;
}
}
return $changes;
}
// }}}
// {{{ verifyAlterDatabase()
/**
* Verify that the changes requested are supported
*
* @param array $changes associative array that contains the definition of the changes
* that are meant to be applied to the database structure.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function verifyAlterDatabase($changes)
{
if (!empty($changes['tables']['change']) && is_array($changes['tables']['change'])) {
foreach ($changes['tables']['change'] as $table_name => $table) {
if (!empty($table['indexes']) && is_array($table['indexes'])) {
if (!$this->db->supports('indexes')) {
return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
'indexes are not supported');
}
$table_changes = count($table['indexes']);
if (!empty($table['indexes']['add'])) {
$table_changes--;
}
if (!empty($table['indexes']['remove'])) {
$table_changes--;
}
if (!empty($table['indexes']['change'])) {
$table_changes--;
}
if ($table_changes) {
return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
'index alteration not yet supported: '.implode(', ', array_keys($table['indexes'])));
}
}
unset($table['indexes']);
$result = $this->db->manager->alterTable($table_name, $table, true);
if (PEAR::isError($result)) {
return $result;
}
}
}
if (!empty($changes['sequences']) && is_array($changes['sequences'])) {
if (!$this->db->supports('sequences')) {
return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
'sequences are not supported');
}
$sequence_changes = count($changes['sequences']);
if (!empty($changes['sequences']['add'])) {
$sequence_changes--;
}
if (!empty($changes['sequences']['remove'])) {
$sequence_changes--;
}
if (!empty($changes['sequences']['change'])) {
$sequence_changes--;
}
if ($sequence_changes) {
return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
'sequence alteration not yet supported: '.implode(', ', array_keys($changes['sequences'])));
}
}
return MDB2_OK;
}
// }}}
// {{{ alterDatabaseIndexes()
/**
* Execute the necessary actions to implement the requested changes
* in the indexes inside a database structure.
*
* @param string $table_name name of the table
* @param array $changes associative array that contains the definition of the changes
* that are meant to be applied to the database structure.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function alterDatabaseIndexes($table_name, $changes)
{
$alterations = 0;
if (empty($changes)) {
return $alterations;
}
if (!empty($changes['remove']) && is_array($changes['remove'])) {
foreach ($changes['remove'] as $index_name => $index) {
$this->db->expectError(MDB2_ERROR_NOT_FOUND);
if (!empty($index['primary']) || !empty($index['unique'])) {
$result = $this->db->manager->dropConstraint($table_name, $index_name, !empty($index['primary']));
} else {
$result = $this->db->manager->dropIndex($table_name, $index_name);
}
$this->db->popExpect();
if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_NOT_FOUND)) {
return $result;
}
$alterations++;
}
}
if (!empty($changes['change']) && is_array($changes['change'])) {
foreach ($changes['change'] as $index_name => $index) {
/**
* Drop existing index/constraint first.
* Since $changes doesn't tell us whether it's an index or a constraint before the change,
* we have to find out and call the appropriate method.
*/
if (in_array($index_name, $this->db->manager->listTableIndexes($table_name))) {
$result = $this->db->manager->dropIndex($table_name, $index_name);
} elseif (in_array($index_name, $this->db->manager->listTableConstraints($table_name))) {
$result = $this->db->manager->dropConstraint($table_name, $index_name);
}
if (!empty($result) && PEAR::isError($result)) {
return $result;
}
if (!empty($index['primary']) || !empty($index['unique'])) {
$result = $this->db->manager->createConstraint($table_name, $index_name, $index);
} else {
$result = $this->db->manager->createIndex($table_name, $index_name, $index);
}
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
}
}
if (!empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $index_name => $index) {
if (!empty($index['primary']) || !empty($index['unique'])) {
$result = $this->db->manager->createConstraint($table_name, $index_name, $index);
} else {
$result = $this->db->manager->createIndex($table_name, $index_name, $index);
}
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
}
}
return $alterations;
}
// }}}
// {{{ alterDatabaseTables()
/**
* Execute the necessary actions to implement the requested changes
* in the tables inside a database structure.
*
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
* @param array $changes associative array that contains the definition of the changes
* that are meant to be applied to the database structure.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function alterDatabaseTables($current_definition, $previous_definition, $changes)
{
/* FIXME: tables marked to be added are initialized by createTable(), others don't */
$alterations = 0;
if (empty($changes)) {
return $alterations;
}
if (!empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $table_name => $table) {
$result = $this->createTable($table_name, $current_definition[$table_name]);
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
}
}
if ($this->options['drop_missing_tables']
&& !empty($changes['remove'])
&& is_array($changes['remove'])) {
foreach ($changes['remove'] as $table_name => $table) {
$result = $this->db->manager->dropTable($table_name);
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
}
}
if (!empty($changes['change']) && is_array($changes['change'])) {
foreach ($changes['change'] as $table_name => $table) {
$indexes = array();
if (!empty($table['indexes'])) {
$indexes = $table['indexes'];
unset($table['indexes']);
}
if (!empty($indexes['remove'])) {
$result = $this->alterDatabaseIndexes($table_name, array('remove' => $indexes['remove']));
if (PEAR::isError($result)) {
return $result;
}
unset($indexes['remove']);
$alterations += $result;
}
$result = $this->db->manager->alterTable($table_name, $table, false);
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
// table may be renamed at this point
if (!empty($table['name'])) {
$table_name = $table['name'];
}
if (!empty($indexes)) {
$result = $this->alterDatabaseIndexes($table_name, $indexes);
if (PEAR::isError($result)) {
return $result;
}
$alterations += $result;
}
}
}
return $alterations;
}
// }}}
// {{{ alterDatabaseSequences()
/**
* Execute the necessary actions to implement the requested changes
* in the sequences inside a database structure.
*
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
* @param array $changes associative array that contains the definition of the changes
* that are meant to be applied to the database structure.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function alterDatabaseSequences($current_definition, $previous_definition, $changes)
{
$alterations = 0;
if (empty($changes)) {
return $alterations;
}
if (!empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $sequence_name => $sequence) {
$result = $this->createSequence($sequence_name, $current_definition[$sequence_name]);
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
}
}
if (!empty($changes['remove']) && is_array($changes['remove'])) {
foreach ($changes['remove'] as $sequence_name => $sequence) {
$result = $this->db->manager->dropSequence($sequence_name);
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
}
}
if (!empty($changes['change']) && is_array($changes['change'])) {
foreach ($changes['change'] as $sequence_name => $sequence) {
$result = $this->db->manager->dropSequence($previous_definition[$sequence_name]['was']);
if (PEAR::isError($result)) {
return $result;
}
$result = $this->createSequence($sequence_name, $sequence);
if (PEAR::isError($result)) {
return $result;
}
$alterations++;
}
}
return $alterations;
}
// }}}
// {{{ alterDatabase()
/**
* Execute the necessary actions to implement the requested changes
* in a database structure.
*
* @param array $current_definition multi dimensional array that contains the current definition
* @param array $previous_definition multi dimensional array that contains the previous definition
* @param array $changes associative array that contains the definition of the changes
* that are meant to be applied to the database structure.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function alterDatabase($current_definition, $previous_definition, $changes)
{
$alterations = 0;
if (empty($changes)) {
return $alterations;
}
$result = $this->verifyAlterDatabase($changes);
if (PEAR::isError($result)) {
return $result;
}
if (!empty($current_definition['name'])) {
$previous_database_name = $this->db->setDatabase($current_definition['name']);
}
if (($support_transactions = $this->db->supports('transactions'))
&& PEAR::isError($result = $this->db->beginNestedTransaction())
) {
return $result;
}
if (!empty($changes['tables']) && !empty($current_definition['tables'])) {
$current_tables = isset($current_definition['tables']) ? $current_definition['tables'] : array();
$previous_tables = isset($previous_definition['tables']) ? $previous_definition['tables'] : array();
$result = $this->alterDatabaseTables($current_tables, $previous_tables, $changes['tables']);
if (is_numeric($result)) {
$alterations += $result;
}
}
if (!PEAR::isError($result) && !empty($changes['sequences'])) {
$current_sequences = isset($current_definition['sequences']) ? $current_definition['sequences'] : array();
$previous_sequences = isset($previous_definition['sequences']) ? $previous_definition['sequences'] : array();
$result = $this->alterDatabaseSequences($current_sequences, $previous_sequences, $changes['sequences']);
if (is_numeric($result)) {
$alterations += $result;
}
}
if ($support_transactions) {
$res = $this->db->completeNestedTransaction();
if (PEAR::isError($res)) {
$result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
'Could not end transaction ('.
$res->getMessage().' ('.$res->getUserinfo().'))');
}
} elseif (PEAR::isError($result) && $alterations) {
$result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
'the requested database alterations were only partially implemented ('.
$result->getMessage().' ('.$result->getUserinfo().'))');
}
if (isset($previous_database_name)) {
$this->db->setDatabase($previous_database_name);
}
return $result;
}
// }}}
// {{{ dumpDatabaseChanges()
/**
* Dump the changes between two database definitions.
*
* @param array $changes associative array that specifies the list of database
* definitions changes as returned by the _compareDefinitions
* manager class function.
*
* @return bool|MDB2_Error MDB2_OK or error object
* @access public
*/
function dumpDatabaseChanges($changes)
{
if (!empty($changes['tables'])) {
if (!empty($changes['tables']['add']) && is_array($changes['tables']['add'])) {
foreach ($changes['tables']['add'] as $table_name => $table) {
$this->db->debug("$table_name:", __FUNCTION__);
$this->db->debug("\tAdded table '$table_name'", __FUNCTION__);
}
}
if (!empty($changes['tables']['remove']) && is_array($changes['tables']['remove'])) {
if ($this->options['drop_missing_tables']) {
foreach ($changes['tables']['remove'] as $table_name => $table) {
$this->db->debug("$table_name:", __FUNCTION__);
$this->db->debug("\tRemoved table '$table_name'", __FUNCTION__);
}
} else {
foreach ($changes['tables']['remove'] as $table_name => $table) {
$this->db->debug("\tObsolete table '$table_name' left as is", __FUNCTION__);
}
}
}
if (!empty($changes['tables']['change']) && is_array($changes['tables']['change'])) {
foreach ($changes['tables']['change'] as $table_name => $table) {
if (array_key_exists('name', $table)) {
$this->db->debug("\tRenamed table '$table_name' to '".$table['name']."'", __FUNCTION__);
}
if (!empty($table['add']) && is_array($table['add'])) {
foreach ($table['add'] as $field_name => $field) {
$this->db->debug("\tAdded field '".$field_name."'", __FUNCTION__);
}
}
if (!empty($table['remove']) && is_array($table['remove'])) {
foreach ($table['remove'] as $field_name => $field) {
$this->db->debug("\tRemoved field '".$field_name."'", __FUNCTION__);
}
}
if (!empty($table['rename']) && is_array($table['rename'])) {
foreach ($table['rename'] as $field_name => $field) {
$this->db->debug("\tRenamed field '".$field_name."' to '".$field['name']."'", __FUNCTION__);
}
}
if (!empty($table['change']) && is_array($table['change'])) {
foreach ($table['change'] as $field_name => $field) {
$field = $field['definition'];
if (array_key_exists('type', $field)) {
$this->db->debug("\tChanged field '$field_name' type to '".$field['type']."'", __FUNCTION__);
}
if (array_key_exists('unsigned', $field)) {
$this->db->debug("\tChanged field '$field_name' type to '".
(!empty($field['unsigned']) && $field['unsigned'] ? '' : 'not ')."unsigned'",
__FUNCTION__);
}
if (array_key_exists('length', $field)) {
$this->db->debug("\tChanged field '$field_name' length to '".
(!empty($field['length']) ? $field['length']: 'no length')."'", __FUNCTION__);
}
if (array_key_exists('default', $field)) {
$this->db->debug("\tChanged field '$field_name' default to ".
(isset($field['default']) ? "'".$field['default']."'" : 'NULL'), __FUNCTION__);
}
if (array_key_exists('notnull', $field)) {
$this->db->debug("\tChanged field '$field_name' notnull to ".
(!empty($field['notnull']) && $field['notnull'] ? 'true' : 'false'),
__FUNCTION__);
}
}
}
if (!empty($table['indexes']) && is_array($table['indexes'])) {
if (!empty($table['indexes']['add']) && is_array($table['indexes']['add'])) {
foreach ($table['indexes']['add'] as $index_name => $index) {
$this->db->debug("\tAdded index '".$index_name.
"' of table '$table_name'", __FUNCTION__);
}
}
if (!empty($table['indexes']['remove']) && is_array($table['indexes']['remove'])) {
foreach ($table['indexes']['remove'] as $index_name => $index) {
$this->db->debug("\tRemoved index '".$index_name.
"' of table '$table_name'", __FUNCTION__);
}
}
if (!empty($table['indexes']['change']) && is_array($table['indexes']['change'])) {
foreach ($table['indexes']['change'] as $index_name => $index) {
if (array_key_exists('name', $index)) {
$this->db->debug("\tRenamed index '".$index_name."' to '".$index['name'].
"' on table '$table_name'", __FUNCTION__);
}
if (array_key_exists('unique', $index)) {
$this->db->debug("\tChanged index '".$index_name."' unique to '".
!empty($index['unique'])."' on table '$table_name'", __FUNCTION__);
}
if (array_key_exists('primary', $index)) {
$this->db->debug("\tChanged index '".$index_name."' primary to '".
!empty($index['primary'])."' on table '$table_name'", __FUNCTION__);
}
if (array_key_exists('change', $index)) {
$this->db->debug("\tChanged index '".$index_name.
"' on table '$table_name'", __FUNCTION__);
}
}
}
}
}
}
}
if (!empty($changes['sequences'])) {
if (!empty($changes['sequences']['add']) && is_array($changes['sequences']['add'])) {
foreach ($changes['sequences']['add'] as $sequence_name => $sequence) {
$this->db->debug("$sequence_name:", __FUNCTION__);
$this->db->debug("\tAdded sequence '$sequence_name'", __FUNCTION__);
}
}
if (!empty($changes['sequences']['remove']) && is_array($changes['sequences']['remove'])) {
foreach ($changes['sequences']['remove'] as $sequence_name => $sequence) {
$this->db->debug("$sequence_name:", __FUNCTION__);
$this->db->debug("\tAdded sequence '$sequence_name'", __FUNCTION__);
}
}
if (!empty($changes['sequences']['change']) && is_array($changes['sequences']['change'])) {
foreach ($changes['sequences']['change'] as $sequence_name => $sequence) {
if (array_key_exists('name', $sequence)) {
$this->db->debug("\tRenamed sequence '$sequence_name' to '".
$sequence['name']."'", __FUNCTION__);
}
if (!empty($sequence['change']) && is_array($sequence['change'])) {
foreach ($sequence['change'] as $sequence_name => $sequence) {
if (array_key_exists('start', $sequence)) {
$this->db->debug("\tChanged sequence '$sequence_name' start to '".
$sequence['start']."'", __FUNCTION__);
}
}
}
}
}
}
return MDB2_OK;
}
// }}}
// {{{ dumpDatabase()
/**
* Dump a previously parsed database structure in the Metabase schema
* XML based format suitable for the Metabase parser.
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment