mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-03-13 08:00:15 +00:00
What the plans for PdoQuery have turned into...
This commit is contained in:
parent
ba4792e38d
commit
6fee9fcb02
293
inc/AwlDBDialect.php
Normal file
293
inc/AwlDBDialect.php
Normal file
@ -0,0 +1,293 @@
|
||||
<?php
|
||||
/**
|
||||
* AwlDatabase - support for different SQL dialects
|
||||
*
|
||||
* This subpackage provides dialect specific support for PostgreSQL, and
|
||||
* may, over time, be extended to provide support for other SQL dialects.
|
||||
*
|
||||
* See http://wiki.davical.org/w/Coding/PdoDatabase for design and usage information.
|
||||
*
|
||||
* @package awl
|
||||
* @subpackage AwlDatabase
|
||||
* @author Andrew McMillan <andrew@morphoss.com>
|
||||
* @copyright Morphoss Ltd
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @compatibility Requires PHP 5.1 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* The AwlDBDialect class handles
|
||||
* @package awl
|
||||
*/
|
||||
class AwlDBDialect {
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds the name of the database dialect
|
||||
*/
|
||||
protected $dialect;
|
||||
|
||||
/**
|
||||
* Holds the PDO database connection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Holds the version
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**#@-*/
|
||||
|
||||
|
||||
/**
|
||||
* Parses the connection string to ascertain the database dialect. Returns true if the dialect is supported
|
||||
* and fails if the dialect is not supported. All code to support any given database should be within in an
|
||||
* external include.
|
||||
*
|
||||
* The database will be opened.
|
||||
*
|
||||
* @param string $connection_string The PDO connection string, in all it's glory
|
||||
* @param string $dbuser The database username to connect as
|
||||
* @param string $dbpass The database password to connect with
|
||||
* @param array $options An array of driver options
|
||||
*/
|
||||
function __construct( $connection_string, $dbuser=null, $dbpass=null, $options=null ) {
|
||||
if ( preg_match( '/^(pgsql):/', $connection_string, $matches ) ) {
|
||||
$this->dialect = $matches[1];
|
||||
}
|
||||
else {
|
||||
trigger_error("Unsupported database connection '".$connection_string."'", E_USER_ERROR);
|
||||
}
|
||||
$this->db = new PDO( $connection_string, $dbuser, $dbpass, $options );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current search path for the database.
|
||||
*/
|
||||
function SetSearchPath( $search_path = null ) {
|
||||
if ( !isset($this->dialect) ) {
|
||||
trigger_error("Unsupported database dialect", E_USER_ERROR);
|
||||
}
|
||||
|
||||
switch ( $this->dialect ) {
|
||||
case 'pgsql':
|
||||
if ( $search_path == null ) $search_path = 'public';
|
||||
$sql = "SET search_path TO " . $this->Quote( $search_path, 'identifier' );
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current search path for the database.
|
||||
* @param handle $pdo A handle to an opened database
|
||||
*/
|
||||
function GetVersion( ) {
|
||||
if ( isset($this->version) ) return $this->version;
|
||||
if ( !isset($this->dialect) ) {
|
||||
trigger_error("Unsupported database dialect", E_USER_ERROR);
|
||||
}
|
||||
|
||||
$version = $this->dialect.':';
|
||||
|
||||
switch ( $this->dialect ) {
|
||||
case 'pgsql':
|
||||
$sql = "SELECT version()";
|
||||
if ( $sth = $this->db->query($sql) ) {
|
||||
$row = $sth->fetch(PDO::FETCH_NUM);
|
||||
$version .= preg_replace( '/^PostgreSQL (\d+\.\d+)\..*$/i', '$1', $row[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
$this->version = $version;
|
||||
return $version;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the SQL for the current database dialect which will return a two-column resultset containing a
|
||||
* list of fields and their associated data types.
|
||||
* @param string $tablename_string The name of the table we want fields from
|
||||
*/
|
||||
function GetFields( $tablename_string ) {
|
||||
if ( !isset($this->dialect) ) {
|
||||
trigger_error("Unsupported database dialect", E_USER_ERROR);
|
||||
}
|
||||
|
||||
switch ( $this->dialect ) {
|
||||
case 'pgsql':
|
||||
$tablename_string = $this->Quote($tablename_string, 'identifier');
|
||||
$sql = "SELECT f.attname, t.typname FROM pg_attribute f ";
|
||||
$sql .= "JOIN pg_class c ON ( f.attrelid = c.oid ) ";
|
||||
$sql .= "JOIN pg_type t ON ( f.atttypid = t.oid ) ";
|
||||
$sql .= "WHERE relname = $tablename_string AND attnum >= 0 order by f.attnum;";
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translates the given SQL string into a form that will hopefully work for this database dialect. This hook
|
||||
* is intended to be used by developers to provide support for differences in database operation by translating
|
||||
* the query string in an arbitrary way, such as through a file or database lookup.
|
||||
*
|
||||
* The actual translation to other SQL dialects will be application-specific, so that any routines
|
||||
* called by this will be external to this library, or will use resources loaded from some source
|
||||
* external to this library.
|
||||
*
|
||||
* The application developer is expected to use this functionality to solve harder translation problems,
|
||||
* but is less likely to call this directly, hopefully switching ->Prepare to ->PrepareTranslated in those
|
||||
* cases, and then adding that statement to whatever SQL translation infrastructure is in place.
|
||||
*/
|
||||
function TranslateSQL( $sql_string ) {
|
||||
// Noop for the time being...
|
||||
return $sql_string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns $value escaped in an appropriate way for this database dialect.
|
||||
* @param mixed $value The value to be escaped
|
||||
* @param string $value_type The type of escaping desired. If blank this will be worked out from gettype($value). The special
|
||||
* type of 'identifier' can also be used for escaping of SQL identifiers.
|
||||
*/
|
||||
function Quote( $value, $value_type = null ) {
|
||||
if ( isset($value_type) && $value_type == 'identifier' ) {
|
||||
if ( $this->dialect == 'mysql' ) {
|
||||
/** TODO: Someone should confirm this is correct for MySql */
|
||||
$rv = '`' . str_replace('`', '\\`', $value ) . '`';
|
||||
}
|
||||
else {
|
||||
$rv = '"' . str_replace('"', '\\"', $value ) . '"';
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
|
||||
switch ( $this->dialect ) {
|
||||
case 'mysql':
|
||||
case 'pgsql':
|
||||
case 'sqlite':
|
||||
if ( is_string($value_type) ) {
|
||||
switch( $value_type ) {
|
||||
case 'null':
|
||||
$value_type = PDO::PARAM_NULL;
|
||||
break;
|
||||
case 'integer':
|
||||
case 'double' :
|
||||
$value_type = PDO::PARAM_INT;
|
||||
break;
|
||||
case 'boolean':
|
||||
$value_type = PDO::PARAM_BOOL;
|
||||
break;
|
||||
case 'string':
|
||||
$value_type = PDO::PARAM_STR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$rv = $this->db->quote($value);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( !isset($value_type) ) {
|
||||
$value_type = gettype($value);
|
||||
}
|
||||
switch ( $value_type ) {
|
||||
case PDO::PARAM_NULL:
|
||||
case 'null':
|
||||
$rv = 'NULL';
|
||||
break;
|
||||
case PDO::PARAM_INT:
|
||||
case 'integer':
|
||||
case 'double' :
|
||||
return $str;
|
||||
case PDO::PARAM_BOOL:
|
||||
case 'boolean':
|
||||
$rv = $str ? 'TRUE' : 'FALSE';
|
||||
break;
|
||||
case PDO::PARAM_STR:
|
||||
case 'string':
|
||||
default:
|
||||
$str = str_replace("'", "''", $str);
|
||||
if ( strpos( $str, '\\' ) !== false ) {
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
if ( $this->dialect == 'pgsql' ) {
|
||||
/** PostgreSQL wants to know when a string might contain escapes */
|
||||
$rv = "E'$str'";
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $rv;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces query parameters with appropriately escaped substitutions.
|
||||
*
|
||||
* The function takes a variable number of arguments, the first is the
|
||||
* SQL string, with replaceable '?' characters (a la DBI). The subsequent
|
||||
* parameters being the values to replace into the SQL string.
|
||||
*
|
||||
* The values passed to the routine are analyzed for type, and quoted if
|
||||
* they appear to need quoting. This can go wrong for (e.g.) NULL or
|
||||
* other special SQL values which are not straightforwardly identifiable
|
||||
* as needing quoting (or not). In such cases the parameter can be forced
|
||||
* to be inserted unquoted by passing it as "array( 'plain' => $param )".
|
||||
*
|
||||
* @param string The query string with replacable '?' characters.
|
||||
* @param mixed The values to replace into the SQL string.
|
||||
* @return The built query string
|
||||
*/
|
||||
function ReplaceParameters() {
|
||||
$argc = func_num_args();
|
||||
$qry = func_get_arg(0);
|
||||
$args = func_get_args();
|
||||
|
||||
if ( is_array($qry) ) {
|
||||
/**
|
||||
* If the first argument is an array we treat that as our arguments instead
|
||||
*/
|
||||
$qry = $args[0][0];
|
||||
$args = $args[0];
|
||||
$argc = count($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* We only split into a maximum of $argc chunks. Any leftover ? will remain in
|
||||
* the string and may be replaced at Exec rather than Prepare.
|
||||
*/
|
||||
$parts = explode( '?', $qry, $argc );
|
||||
$querystring = $parts[0];
|
||||
$z = count($parts);
|
||||
|
||||
for( $i = 1; $i < $z; $i++ ) {
|
||||
$arg = $args[$i];
|
||||
if ( !isset($arg) ) {
|
||||
$querystring .= 'NULL';
|
||||
}
|
||||
elseif ( is_array($arg) && $arg['plain'] != '' ) {
|
||||
// We abuse this, but people should access it through the PgQuery::Plain($v) function
|
||||
$querystring .= $arg['plain'];
|
||||
}
|
||||
else {
|
||||
$querystring .= $this->Quote($arg); //parameter
|
||||
}
|
||||
$querystring .= $parts[$i]; //extras eg. ","
|
||||
}
|
||||
if ( isset($parts[$z]) ) $querystring .= $parts[$z]; //puts last part on the end
|
||||
|
||||
return $querystring;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
152
inc/AwlDatabase.php
Normal file
152
inc/AwlDatabase.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* AwlDatabase query/statement class and associated functions
|
||||
*
|
||||
* This subpackage provides some functions that are useful around database
|
||||
* activity and a AwlDialect, AwlDatabase and AwlStatement classes to simplify
|
||||
* handling of database queries and provide some access for a limited
|
||||
* ability to handle varying database dialects.
|
||||
*
|
||||
* The class is intended to be a very lightweight wrapper with some features
|
||||
* that have proved useful in developing and debugging web-based applications:
|
||||
* - All queries are timed, and an expected time can be provided.
|
||||
* - Parameters replaced into the SQL will be escaped correctly in order to
|
||||
* minimise the chances of SQL injection errors.
|
||||
* - Queries which fail, or which exceed their expected execution time, will
|
||||
* be logged for potential further analysis.
|
||||
* - Debug logging of queries may be enabled globally, or restricted to
|
||||
* particular sets of queries.
|
||||
* - Simple syntax for iterating through a result set.
|
||||
*
|
||||
* See http://wiki.davical.org/w/AwlDatabase for design and usage information.
|
||||
*
|
||||
* If not already connected, AwlDatabase will attempt to connect to the database,
|
||||
* successively applying connection parameters from the array in $c->pdo_connect.
|
||||
*
|
||||
* We will die if the database is not currently connected and we fail to find
|
||||
* a working connection.
|
||||
*
|
||||
* @package awl
|
||||
* @subpackage AwlDatabase
|
||||
* @author Andrew McMillan <andrew@morphoss.com>
|
||||
* @copyright Morphoss Ltd
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @compatibility Requires PHP 5.1 or later
|
||||
*/
|
||||
|
||||
if ( !class_exists('AwlDBDialect') ) require('AwlDBDialect.php');
|
||||
|
||||
/**
|
||||
* Methods in the AwlDBDialect class which we inherit, include:
|
||||
* __construct()
|
||||
* SetSearchPath( $search_path )
|
||||
* GetVersion()
|
||||
* GetFields( $tablename_string )
|
||||
* TranslateSQL( $sql_string )
|
||||
* Quote( $value, $value_type = null )
|
||||
* ReplaceParameters( $query_string [, param [, ...]] )
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Typically there will only be a single instance of the database level class in an application.
|
||||
* @package awl
|
||||
*/
|
||||
class AwlDatabase extends AwlDBDialect {
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds the state of the transaction 0 = not started, 1 = in progress, -1 = error pending rollback/commit
|
||||
*/
|
||||
protected $txnstate = 0;
|
||||
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Returns a PDOStatement object created using this database, the supplied SQL string, and any parameters given.
|
||||
* @param string $sql_query_string The SQL string containing optional variable replacements
|
||||
* @param array $driver_options PDO driver options to the prepare statement, commonly to do with cursors
|
||||
*/
|
||||
function prepare( $statement, $driver_options = array() ) {
|
||||
return $this->db->prepare( $statement, $driver_options );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a PDOStatement object created using this database, the supplied SQL string, and any parameters given.
|
||||
* @param string $sql_query_string The SQL string containing optional variable replacements
|
||||
* @param mixed ... Subsequent arguments are positionally replaced into the $sql_query_string
|
||||
*/
|
||||
function query( $statement ) {
|
||||
return $this->db->query( $statement );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begin a transaction.
|
||||
*/
|
||||
function Begin() {
|
||||
if ( $this->txnstate == 0 ) {
|
||||
$this->db->beginTransaction();
|
||||
$this->txnstate = 1;
|
||||
}
|
||||
else {
|
||||
trigger_error("Cannot begin a transaction while a transaction is already active.", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Complete a transaction.
|
||||
*/
|
||||
function Commit() {
|
||||
if ( $this->txnstate != 0 ) {
|
||||
$this->db->commit();
|
||||
$this->txnstate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a transaction in progress.
|
||||
*/
|
||||
function Rollback() {
|
||||
if ( $this->txnstate != 0 ) {
|
||||
$this->db->rollBack();
|
||||
$this->txnstate = 0;
|
||||
}
|
||||
else {
|
||||
trigger_error("Cannot rollback unless a transaction is already active.", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current state of a transaction, indicating if we have begun a transaction, whether the transaction
|
||||
* has failed, or if we are not in a transaction.
|
||||
*/
|
||||
function TransactionState() {
|
||||
return $this->txnstate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Operates identically to AwlDatabase::Prepare, except that $this->Translate() will be called on the query
|
||||
* before any processing.
|
||||
*/
|
||||
function PrepareTranslated() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switches on or off the processing flag controlling whether subsequent calls to AwlDatabase::Prepare are translated
|
||||
* as if PrepareTranslated() had been called.
|
||||
*/
|
||||
function TranslateAll( $onoff_boolean ) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
438
inc/AwlQuery.php
Normal file
438
inc/AwlQuery.php
Normal file
@ -0,0 +1,438 @@
|
||||
<?php
|
||||
/**
|
||||
* @package awl
|
||||
* @subpackage AWLDB
|
||||
* @author Andrew McMillan <andrew@morphoss.com>
|
||||
* @copyright Morphoss Ltd
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @compatibility Requires PHP 5.1 or later
|
||||
*/
|
||||
|
||||
require_once('AwlDatabase.php');
|
||||
|
||||
/**
|
||||
* Database query class and associated functions
|
||||
*
|
||||
* This subpackage provides some functions that are useful around database
|
||||
* activity and an AwlQuery class to simplify handling of database queries.
|
||||
*
|
||||
* The class is intended to be a very lightweight wrapper with no pretentions
|
||||
* towards database independence, but it does include some features that have
|
||||
* proved useful in developing and debugging web-based applications:
|
||||
* - All queries are timed, and an expected time can be provided.
|
||||
* - Parameters replaced into the SQL will be escaped correctly in order to
|
||||
* minimise the chances of SQL injection errors.
|
||||
* - Queries which fail, or which exceed their expected execution time, will
|
||||
* be logged for potential further analysis.
|
||||
* - Debug logging of queries may be enabled globally, or restricted to
|
||||
* particular sets of queries.
|
||||
* - Simple syntax for iterating through a result set.
|
||||
*
|
||||
* This class is intended as a transitional mechanism for moving from the
|
||||
* PostgreSQL-specific PgQuery class to something which uses PDO in a more
|
||||
* replaceable manner.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Connect to the database defined in the $c->db_connect[] (or $c->pg_connect) arrays
|
||||
*/
|
||||
function _awl_connect_configured_database() {
|
||||
global $c, $_awl_dbconn;
|
||||
|
||||
/**
|
||||
* Attempt to connect to the configured connect strings
|
||||
*/
|
||||
$_awl_dbconn = false;
|
||||
|
||||
if ( isset($c->db_connect) ) {
|
||||
$connection_strings = $c->db_connect;
|
||||
}
|
||||
elseif ( isset($c->pg_connect) ) {
|
||||
$connection_strings = $c->pg_connect;
|
||||
}
|
||||
|
||||
foreach( $connection_strings AS $k => $v ) {
|
||||
$dbuser = null;
|
||||
$dbpass = null;
|
||||
if ( is_array($v) ) {
|
||||
$dsn = $v['dsn'];
|
||||
if ( isset($v['dbuser']) ) $dbuser = $v['dbuser'];
|
||||
if ( isset($v['dbpass']) ) $dbpass = $v['dbpass'];
|
||||
}
|
||||
elseif ( preg_match( '/^(\S+:)?(.*)( user=(\S+))?( password=(\S+))?$/', $v, $matches ) ) {
|
||||
$dsn = $matches[2];
|
||||
if ( isset($matches[1]) && $matches[1] != '' ) {
|
||||
$dsn = $matches[1] . $dsn;
|
||||
}
|
||||
else {
|
||||
$dsn = 'pgsql:' . $dsn;
|
||||
}
|
||||
if ( isset($matches[4]) && $matches[4] != '' ) $dbuser = $matches[4];
|
||||
if ( isset($matches[6]) && $matches[6] != '' ) $dbpass = $matches[6];
|
||||
}
|
||||
if ( $_awl_dbconn = new AwlDatabase( $dsn, $dbuser, $dbpass ) ) break;
|
||||
}
|
||||
|
||||
if ( ! $_awl_dbconn ) {
|
||||
echo <<<EOERRMSG
|
||||
<html><head><title>Database Connection Failure</title></head><body>
|
||||
<h1>Database Error</h1>
|
||||
<h3>Could not connect to database</h3>
|
||||
</body>
|
||||
</html>
|
||||
EOERRMSG;
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( isset($c->db_schema) && $c->db_schema != '' ) {
|
||||
$_awl_dbconn->SetSearchPath( $c->db_schema . ',public' );
|
||||
}
|
||||
|
||||
$c->_awl_dbversion = $_awl_dbconn->GetVersion();
|
||||
}
|
||||
|
||||
|
||||
if ( !function_exists('duration') ) {
|
||||
/**
|
||||
* A duration (in decimal seconds) between two times which are the result of calls to microtime()
|
||||
*
|
||||
* This simple function is used by the AwlQuery class because the
|
||||
* microtime function doesn't return a decimal time, so a simple
|
||||
* subtraction is not sufficient.
|
||||
*
|
||||
* @param microtime $t1 start time
|
||||
* @param microtime $t2 end time
|
||||
* @return double difference
|
||||
*/
|
||||
function duration( $t1, $t2 ) {
|
||||
list ( $ms1, $s1 ) = explode ( " ", $t1 ); // Format times - by spliting seconds and microseconds
|
||||
list ( $ms2, $s2 ) = explode ( " ", $t2 );
|
||||
$s1 = $s2 - $s1;
|
||||
$s1 = $s1 + ( $ms2 -$ms1 );
|
||||
return $s1; // Return duration of time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The AwlQuery Class.
|
||||
*
|
||||
* This class builds and executes SQL Queries and traverses the
|
||||
* set of results returned from the query.
|
||||
*
|
||||
* <b>Example usage</b>
|
||||
* <code>
|
||||
* $sql = "SELECT * FROM mytable WHERE mytype = ?";
|
||||
* $qry = new AwlQuery( $sql, $myunsanitisedtype );
|
||||
* if ( $qry->Exec("typeselect", __line__, __file__ )
|
||||
* && $qry->rows > 0 )
|
||||
* {
|
||||
* while( $row = $qry->Fetch() ) {
|
||||
* do_something_with($row);
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @package awl
|
||||
*/
|
||||
class AwlQuery
|
||||
{
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
/**
|
||||
* Our database connection, normally copied from a global one
|
||||
* @var resource
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The original query string
|
||||
* @var string
|
||||
*/
|
||||
protected $querystring;
|
||||
|
||||
/**
|
||||
* The current array of bound parameters
|
||||
* @var array
|
||||
*/
|
||||
protected $bound_parameters;
|
||||
|
||||
/**
|
||||
* The PDO statement handle, or null if we don't have one yet.
|
||||
* @var string
|
||||
*/
|
||||
protected $sth;
|
||||
|
||||
/**
|
||||
* Result of the last execution
|
||||
* @var resource
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* number of current row - use accessor to get/set
|
||||
* @var int
|
||||
*/
|
||||
protected $rownum = null;
|
||||
|
||||
/**
|
||||
* number of rows from pg_numrows - use accessor to get value
|
||||
* @var int
|
||||
*/
|
||||
protected $rows;
|
||||
|
||||
/**
|
||||
* The Database error information, if the query fails.
|
||||
* @var string
|
||||
*/
|
||||
protected $error_info;
|
||||
|
||||
/**
|
||||
* Stores the query execution time - used to deal with long queries.
|
||||
* should be read-only
|
||||
* @var string
|
||||
*/
|
||||
protected $execution_time;
|
||||
|
||||
/**#@-*/
|
||||
|
||||
/**#@+
|
||||
* @access public
|
||||
*/
|
||||
/**
|
||||
* Where we called this query from so we can find it in our code!
|
||||
* Debugging may also be selectively enabled for a $location.
|
||||
* @var string
|
||||
*/
|
||||
public $location;
|
||||
|
||||
/**
|
||||
* How long the query should take before a warning is issued.
|
||||
*
|
||||
* This is writable, but a method to set it might be a better interface.
|
||||
* The default is 0.3 seconds.
|
||||
* @var double
|
||||
*/
|
||||
public $query_time_warning = 0.3;
|
||||
/**#@-*/
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string The query string in PDO syntax with replacable '?' characters or bindable parameters.
|
||||
* @param mixed The values to replace into the SQL string.
|
||||
* @return The AwlQuery object
|
||||
*/
|
||||
function __construct() {
|
||||
global $_awl_dbconn;
|
||||
$this->rows = null;
|
||||
$this->execution_time = 0;
|
||||
$this->error_info = null;
|
||||
$this->rownum = -1;
|
||||
if ( isset($dbconn) ) $this->connection = $_awl_dbconn;
|
||||
else $this->connection = null;
|
||||
|
||||
$argc = func_num_args();
|
||||
|
||||
$this->querystring = func_get_arg(0);
|
||||
if ( 1 < $argc ) {
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
$this->Bind($args);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use a different database connection for this query
|
||||
* @param resource $new_connection The database connection to use.
|
||||
*/
|
||||
function SetConnection( $new_connection ) {
|
||||
$this->connection = $new_connection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Log query, optionally with file and line location of the caller.
|
||||
*
|
||||
* This function should not really be used outside of AwlQuery. For a more
|
||||
* useful generic logging interface consider calling dbg_error_log(...);
|
||||
*
|
||||
* @param string $locn A string identifying the calling location.
|
||||
* @param string $tag A tag string, e.g. identifying the type of event.
|
||||
* @param string $string The information to be logged.
|
||||
* @param int $line The line number where the logged event occurred.
|
||||
* @param string $file The file name where the logged event occurred.
|
||||
*/
|
||||
function _log_query( $locn, $tag, $string, $line = 0, $file = "") {
|
||||
// replace more than one space with one space
|
||||
$string = preg_replace('/\s+/', ' ', $string);
|
||||
|
||||
if ( ($tag == 'QF' || $tag == 'SQ') && ( $line != 0 && $file != "" ) ) {
|
||||
dbg_error_log( "LOG-$locn", " Query: %s: %s in '%s' on line %d", ($tag == 'QF' ? 'Error' : 'Possible slow query'), $tag, $file, $line );
|
||||
}
|
||||
|
||||
while( strlen( $string ) > 0 ) {
|
||||
dbg_error_log( "LOG-$locn", " Query: %s: %s", $tag, substr( $string, 0, 240) );
|
||||
$string = substr( "$string", 240 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quote the given string so it can be safely used within string delimiters
|
||||
* in a query. To be avoided, in general.
|
||||
*
|
||||
* @param mixed $str Data to be converted to a string suitable for including as a value in SQL.
|
||||
* @return string NULL, TRUE, FALSE, a plain number, or the original string quoted and with ' and \ characters escaped
|
||||
*/
|
||||
function quote($str = null) {
|
||||
if ( !isset($this->connection) ) {
|
||||
_awl_connect_configured_database();
|
||||
$this->connection = $GLOBALS['_awl_dbconn'];
|
||||
}
|
||||
return $this->connection->Quote($str);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind some parameters
|
||||
*/
|
||||
function Bind() {
|
||||
$args = func_get_args();
|
||||
|
||||
if ( gettype($args[0]) == 'array' ) {
|
||||
$this->bound_parameters = $args[0];
|
||||
/** @TODO: perhaps we should WARN here if there is more than 1 argument */
|
||||
}
|
||||
else {
|
||||
$this->bound_parameters = $args;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tell the database to prepare the query that we will execute
|
||||
*/
|
||||
function Prepare() {
|
||||
if ( !isset($this->connection) ) {
|
||||
_awl_connect_configured_database();
|
||||
$this->connection = $GLOBALS['_awl_dbconn'];
|
||||
}
|
||||
$this->sth = $this->connection->prepare( $this->querystring );
|
||||
if ( ! $this->sth ) {
|
||||
$this->error_info = $this->connection->errorInfo();
|
||||
}
|
||||
else $this->error_info = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the query, logging any debugging.
|
||||
*
|
||||
* <b>Example</b>
|
||||
* So that you can nicely enable/disable the queries for a particular class, you
|
||||
* could use some of PHPs magic constants in your call.
|
||||
* <code>
|
||||
* $qry->Exec(__CLASS__, __LINE__, __FILE__);
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* @param string $location The name of the location for enabling debugging or just
|
||||
* to help our children find the source of a problem.
|
||||
* @param int $line The line number where Exec was called
|
||||
* @param string $file The file where Exec was called
|
||||
* @return resource The actual result of the query (FWIW)
|
||||
*/
|
||||
function Exec( $location = '', $line = 0, $file = '' ) {
|
||||
global $debuggroups, $c;
|
||||
$this->location = trim($location);
|
||||
if ( $this->location == "" ) $this->location = substr($_SERVER['PHP_SELF'],1);
|
||||
|
||||
if ( isset($debuggroups['querystring']) || isset($c->dbg['querystring']) || isset($c->dbg['ALL']) ) {
|
||||
$this->_log_query( $this->location, 'DBGQ', $this->querystring, $line, $file );
|
||||
}
|
||||
|
||||
if ( isset($this->bound_parameters) && !isset($this->sth) ) {
|
||||
$this->Prepare();
|
||||
}
|
||||
|
||||
|
||||
$t1 = microtime(true); // get start time
|
||||
if ( isset($this->sth) && $this->sth !== false ) {
|
||||
if ( ! $this->sth->execute( $this->bound_parameters ) ) {
|
||||
$this->error_info = $this->sth->errorInfo();
|
||||
}
|
||||
else $this->error_info = null;
|
||||
}
|
||||
else if ( $this->sth !== false ) {
|
||||
/** Ensure we have a connection to the database */
|
||||
if ( !isset($this->connection) ) {
|
||||
_awl_connect_configured_database();
|
||||
$this->connection = $GLOBALS['_awl_dbconn'];
|
||||
}
|
||||
$this->sth = $this->connection->query( $this->querystring );
|
||||
if ( ! $this->sth ) {
|
||||
$this->error_info = $this->connection->errorInfo();
|
||||
}
|
||||
else $this->error_info = null;
|
||||
}
|
||||
$success = !isset($this->error_info);
|
||||
$this->rows = $this->sth->rowCount();
|
||||
$t2 = microtime(true); // get end time
|
||||
$i_took = $t2 - $t1;
|
||||
$c->total_query_time += $i_took;
|
||||
$this->execution_time = sprintf( "%2.06lf", $i_took);
|
||||
|
||||
if ( ! $success ) {
|
||||
// query failed
|
||||
$this->errorstring = sprintf( 'SQL error "%s" - %s"', $this->error_info[0], $this->error_info[2]);
|
||||
$this->_log_query( $this->location, 'QF', $this->errorstring, $line, $file );
|
||||
}
|
||||
elseif ( $this->execution_time > $this->query_time_warning ) {
|
||||
// if execution time is too long
|
||||
$this->_log_query( $this->location, 'SQ', "Took: $this->execution_time for $this->querystring", $line, $file ); // SQ == Slow Query :-)
|
||||
}
|
||||
elseif ( isset($debuggroups[$this->location]) || isset($c->dbg[strtolower($this->location)]) || isset($c->dbg['ALL']) ) {
|
||||
// query successful, but we're debugging and want to know how long it took anyway
|
||||
$this->_log_query( $this->location, 'DBGQ', "Took: $this->execution_time for $this->querystring to find $this->rows rows.", $line, $file );
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the next row from the query results
|
||||
* @param boolean $as_array True if thing to be returned is array
|
||||
* @return mixed query row
|
||||
*/
|
||||
function Fetch($as_array = false) {
|
||||
global $c, $debuggroups;
|
||||
|
||||
if ( ( isset($debuggroups["$this->location"]) && $debuggroups["$this->location"] > 2 )
|
||||
|| (isset($c) && is_object($c) && ( isset($c->dbg[strtolower($this->location)]) && isset($c->dbg[strtolower($this->location)]) )
|
||||
|| isset($c->dbg['ALL']) ) ) {
|
||||
$this->_log_query( $this->location, "Fetch", "$this->result Rows: $this->rows, Rownum: $this->rownum");
|
||||
}
|
||||
if ( ! $this->sth || $this->rows == 0 ) return false; // no results
|
||||
if ( $this->rownum == null ) $this->rownum = -1;
|
||||
if ( ($this->rownum + 1) >= $this->rows ) return false; // reached the end of results
|
||||
|
||||
$this->rownum++;
|
||||
if ( isset($debuggroups["$this->location"]) && $debuggroups["$this->location"] > 1 ) {
|
||||
$this->_log_query( $this->location, "Fetch", "Fetching row $this->rownum" );
|
||||
}
|
||||
$row = $this->sth->fetch( ($as_array ? PDO::FETCH_NUM : PDO::FETCH_OBJ) );
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
488
inc/PdoQuery.php
488
inc/PdoQuery.php
@ -1,488 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* PDO query class and associated functions
|
||||
*
|
||||
* This subpackage provides some functions that are useful around database
|
||||
* activity and a PdoDialect, PdoDatabase and PdoQuery classes to simplify
|
||||
* handling of database queries and provide some access for a limited
|
||||
* ability to handle varying database dialects.
|
||||
*
|
||||
* The class is intended to be a very lightweight wrapper with some features
|
||||
* that have proved useful in developing and debugging web-based applications:
|
||||
* - All queries are timed, and an expected time can be provided.
|
||||
* - Parameters replaced into the SQL will be escaped correctly in order to
|
||||
* minimise the chances of SQL injection errors.
|
||||
* - Queries which fail, or which exceed their expected execution time, will
|
||||
* be logged for potential further analysis.
|
||||
* - Debug logging of queries may be enabled globally, or restricted to
|
||||
* particular sets of queries.
|
||||
* - Simple syntax for iterating through a result set.
|
||||
*
|
||||
* See http://wiki.davical.org/w/PdoQuery for design and usage information.
|
||||
*
|
||||
* If not already connected, PdoQuery will attempt to connect to the database,
|
||||
* successively applying connection parameters from the array in $c->pdo_connect.
|
||||
*
|
||||
* We will die if the database is not currently connected and we fail to find
|
||||
* a working connection.
|
||||
*
|
||||
* @package awl
|
||||
* @subpackage PdoQuery
|
||||
* @author Andrew McMillan <andrew@morphoss.com>
|
||||
* @copyright Morphoss Ltd
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v3
|
||||
* @compatibility Requires PHP 5.1 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* The PdoDialect class handles
|
||||
* @package awl
|
||||
*/
|
||||
class PdoDialect {
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds the name of the database dialect
|
||||
*/
|
||||
protected $dialect;
|
||||
|
||||
/**#@-*/
|
||||
|
||||
|
||||
/**
|
||||
* Parses the connection string to ascertain the database dialect. Returns true if the dialect is supported
|
||||
* and fails if the dialect is not supported. All code to support any given database should be within in an
|
||||
* external include.
|
||||
* @param string $connection_string The full PDO connection string
|
||||
*/
|
||||
function __construct( $connection_string ) {
|
||||
if ( preg_match( '/^(pgsql):/', $connection_string, $matches ) ) {
|
||||
$this->dialect = $matches[1];
|
||||
}
|
||||
else {
|
||||
trigger_error("Unsupported database connection '".$connection_string."'", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the SQL for the current database dialect which will return a two-column resultset containing a
|
||||
* list of fields and their associated data types.
|
||||
* @param string $tablename_string The name of the table we want fields from
|
||||
*/
|
||||
function GetFields( $tablename_string ) {
|
||||
if ( !isset($this->dialect) ) {
|
||||
trigger_error("Unsupported database dialect", E_USER_ERROR);
|
||||
}
|
||||
|
||||
switch ( $this->dialect ) {
|
||||
case 'pgsql':
|
||||
$tablename_string = $this->Quote($tablename_string, 'identifier');
|
||||
$sql = "SELECT f.attname, t.typname FROM pg_attribute f ";
|
||||
$sql .= "JOIN pg_class c ON ( f.attrelid = c.oid ) ";
|
||||
$sql .= "JOIN pg_type t ON ( f.atttypid = t.oid ) ";
|
||||
$sql .= "WHERE relname = $tablename_string AND attnum >= 0 order by f.attnum;";
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translates the given SQL string into a form that will hopefully work for this database dialect. This hook
|
||||
* is expected to be used by developers to provide support for differences in database operation by translating
|
||||
* the query string in an arbitrary way, such as through a file or database lookup.
|
||||
*
|
||||
* The actual translation to other SQL dialects will usually be application-specific, so that any routines
|
||||
* called by this will usually be external to this library, or will use resources external to this library.
|
||||
*/
|
||||
function Translate( $sql_string ) {
|
||||
// Noop for the time being...
|
||||
return $sql_string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns $value escaped in an appropriate way for this database dialect.
|
||||
* @param mixed $value The value to be escaped
|
||||
* @param string $value_type The type of escaping desired. If blank this will be worked out from gettype($value). The special
|
||||
* type of 'identifier' can also be used for escaping of SQL identifiers.
|
||||
*/
|
||||
function Quote( $value, $value_type = null ) {
|
||||
|
||||
if ( !isset($value_type) ) {
|
||||
$value_type = gettype($value);
|
||||
}
|
||||
|
||||
switch ( $value_type ) {
|
||||
case 'identifier': // special case will only happen if it is passed in.
|
||||
$rv = '"' . str_replace('"', '\\"', $value ) . '"';
|
||||
break;
|
||||
case 'null':
|
||||
$rv = 'NULL';
|
||||
break;
|
||||
case 'integer':
|
||||
case 'double' :
|
||||
return $str;
|
||||
case 'boolean':
|
||||
$rv = $str ? 'TRUE' : 'FALSE';
|
||||
break;
|
||||
case 'string':
|
||||
default:
|
||||
$str = str_replace("'", "''", $str);
|
||||
if ( strpos( $str, '\\' ) !== false ) {
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
if ( $this->dialect == 'pgsql' ) {
|
||||
/** PostgreSQL wants to know when a string might contain escapes */
|
||||
$rv = "E'$str'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rv;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces query parameters with appropriately escaped substitutions.
|
||||
*
|
||||
* The function takes a variable number of arguments, the first is the
|
||||
* SQL string, with replaceable '?' characters (a la DBI). The subsequent
|
||||
* parameters being the values to replace into the SQL string.
|
||||
*
|
||||
* The values passed to the routine are analyzed for type, and quoted if
|
||||
* they appear to need quoting. This can go wrong for (e.g.) NULL or
|
||||
* other special SQL values which are not straightforwardly identifiable
|
||||
* as needing quoting (or not). In such cases the parameter can be forced
|
||||
* to be inserted unquoted by passing it as "array( 'plain' => $param )".
|
||||
*
|
||||
* @param string The query string with replacable '?' characters.
|
||||
* @param mixed The values to replace into the SQL string.
|
||||
* @return The built query string
|
||||
*/
|
||||
function ReplaceParameters() {
|
||||
$argc = func_num_args();
|
||||
$qry = func_get_arg(0);
|
||||
$args = func_get_args();
|
||||
|
||||
if ( is_array($qry) ) {
|
||||
/**
|
||||
* If the first argument is an array we treat that as our arguments instead
|
||||
*/
|
||||
$qry = $args[0][0];
|
||||
$args = $args[0];
|
||||
$argc = count($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* We only split into a maximum of $argc chunks. Any leftover ? will remain in
|
||||
* the string and may be replaced at Exec rather than Prepare.
|
||||
*/
|
||||
$parts = explode( '?', $qry, $argc );
|
||||
$querystring = $parts[0];
|
||||
$z = count($parts);
|
||||
|
||||
for( $i = 1; $i < $z; $i++ ) {
|
||||
$arg = $args[$i];
|
||||
if ( !isset($arg) ) {
|
||||
$querystring .= 'NULL';
|
||||
}
|
||||
elseif ( is_array($arg) && $arg['plain'] != '' ) {
|
||||
// We abuse this, but people should access it through the PgQuery::Plain($v) function
|
||||
$querystring .= $arg['plain'];
|
||||
}
|
||||
else {
|
||||
$querystring .= $this->Quote($arg); //parameter
|
||||
}
|
||||
$querystring .= $parts[$i]; //extras eg. ","
|
||||
}
|
||||
if ( isset($parts[$z]) ) $querystring .= $parts[$z]; //puts last part on the end
|
||||
|
||||
return $querystring;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Typically there will only be a single instance of the database level class in an application.
|
||||
* @package awl
|
||||
*/
|
||||
class PdoDatabase {
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds the PDO database connection
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Holds the dialect object
|
||||
*/
|
||||
private $dialect;
|
||||
|
||||
/**
|
||||
* Holds the state of the transaction 0 = not started, 1 = in progress, -1 = error pending rollback/commit
|
||||
*/
|
||||
protected $txnstate = 0;
|
||||
|
||||
/**
|
||||
* Holds the count of queries executed so far
|
||||
*/
|
||||
protected $querycount = 0;
|
||||
|
||||
/**
|
||||
* Holds the total duration of queries executed so far
|
||||
*/
|
||||
protected $querytime = 0;
|
||||
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* The connection string is in the standard PDO format. The database won't actually be connected until the first
|
||||
* database query is run against it.
|
||||
*
|
||||
* The database object will also initialise and hold an PdoDialect object which will be used to provide database
|
||||
* specific SQL for some queries, as well as translation hooks for instances where it is necessary to modify the
|
||||
* SQL in transit to support additional databases.
|
||||
* @param string $connection_string The PDO connection string, in all it's glory
|
||||
* @param string $dbuser The database username to connect as
|
||||
* @param string $dbpass The database password to connect with
|
||||
* @param array $options An array of driver options
|
||||
*/
|
||||
function __construct( $connection_string, $dbuser=null, $dbpass=null, $options=null ) {
|
||||
$this->dialect = new PdoDialect( $connection_string );
|
||||
$this->db = new PDO( $connection_string, $dbuser, $dbpass, $options );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a PdoQuery object created using this database, the supplied SQL string, and any parameters given.
|
||||
* @param string $sql_query_string The SQL string containing optional variable replacements
|
||||
* @param mixed ... Subsequent arguments are positionally replaced into the $sql_query_string
|
||||
*/
|
||||
function Prepare( ) {
|
||||
$qry = new PdoQuery( $this );
|
||||
$qry->Query(func_get_args());
|
||||
return $qry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct and execute an SQL statement from the sql_string, replacing the parameters into it.
|
||||
*
|
||||
* @param string $sql_query_string The SQL string containing optional variable replacements
|
||||
* @param mixed ... Subsequent arguments are positionally replaced into the $sql_query_string
|
||||
* @return mixed false on error or number of rows affected. Test failure with === false
|
||||
*/
|
||||
function Exec( ) {
|
||||
$sql_string = $this->dialect->ReplaceParameters(func_get_args());
|
||||
|
||||
$start = microtime(true);
|
||||
$result = $db->exec($sql_string);
|
||||
$duration = microtime(true) - $start;
|
||||
$this->querytime += $duration;
|
||||
$this->querycount++;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begin a transaction.
|
||||
*/
|
||||
function Begin() {
|
||||
if ( $this->txnstate == 0 ) {
|
||||
$this->db->beginTransaction();
|
||||
$this->txnstate = 1;
|
||||
}
|
||||
else {
|
||||
trigger_error("Cannot begin a transaction while a transaction is already active.", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Complete a transaction.
|
||||
*/
|
||||
function Commit() {
|
||||
$this->txnstate = 0;
|
||||
if ( $this->txnstate != 0 ) {
|
||||
$this->db->commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a transaction in progress.
|
||||
*/
|
||||
function Rollback() {
|
||||
$this->txnstate = 0;
|
||||
if ( $this->txnstate != 0 ) {
|
||||
$this->db->rollBack();
|
||||
}
|
||||
else {
|
||||
trigger_error("Cannot rollback unless a transaction is already active.", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current state of a transaction, indicating if we have begun a transaction, whether the transaction
|
||||
* has failed, or if we are not in a transaction.
|
||||
*/
|
||||
function TransactionState() {
|
||||
return $this->txnstate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the total duration of quries executed so far by this object instance.
|
||||
*/
|
||||
function TotalDuration() {
|
||||
return $this->querytime;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the total number of quries executed by this object instance.
|
||||
*/
|
||||
function TotalQueries() {
|
||||
return $this->querycount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an associative array of field types, keyed by field name, for the requested named table. Internally this
|
||||
* calls PdoDialect::GetFields to get the required SQL and then processes the query in the normal manner.
|
||||
*/
|
||||
function GetFields( $tablename_string ) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Operates identically to PdoDatabase::Prepare, except that PdoDialect::Translate() will be called on the query
|
||||
* before any processing.
|
||||
*/
|
||||
function PrepareTranslated() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switches on or off the processing flag controlling whether subsequent calls to PdoDatabase::Prepare are translated
|
||||
* as if PrepareTranslated() had been called.
|
||||
*/
|
||||
function TranslateAll( $onoff_boolean ) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A variable of this class is normally constructed through a call to PdoDatabase::Query or PdoDatabase::Prepare,
|
||||
* associating it on construction with the database which is to be queried.
|
||||
* @package awl
|
||||
*/
|
||||
class PdoQuery {
|
||||
|
||||
private $pdb;
|
||||
private $sth;
|
||||
private $max_duration = 2;
|
||||
|
||||
/**
|
||||
* Where $db is a PdoDatabase object. This constructs the PdoQuery. If there are further parameters they
|
||||
* will be in turn, the sql, and any positional parameters to replace into that, and will be passed to
|
||||
* $this->Query() before returning.
|
||||
*/
|
||||
function __construct( ) {
|
||||
$args = func_get_args();
|
||||
$this->pdb = array_shift( $args );
|
||||
if ( isset($db->default_max_duration) ) {
|
||||
$this->max_duration = $db->default_max_duration;
|
||||
}
|
||||
$this->Query($args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the sql is supplied then PDO::prepare will be called with that SQL to prepare the query, and if there
|
||||
* are positional parameters then they will be replaced into the sql_string (with appropriate escaping)
|
||||
* before the call to PDO::prepare. Query preparation time is counted towards total query execution time.
|
||||
*/
|
||||
function Query( ) {
|
||||
$sql_string = $this->dialect->ReplaceParameters(func_get_args());
|
||||
|
||||
$start = microtime(true);
|
||||
$this->sth = $pdb->db->prepare($sql_string);
|
||||
$duration = microtime(true) - $start;
|
||||
$this->querytime += $duration;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If there are (some) positional parameters in the prepared query, now is the last chance to supply them...
|
||||
* before the query is executed. Returns true on success and false on error.
|
||||
*/
|
||||
function Exec( ) {
|
||||
$start = microtime(true);
|
||||
$result = $this->sth->execute(func_get_args());
|
||||
$duration = microtime(true) - $start;
|
||||
$this->querytime += $duration;
|
||||
$this->querycount++;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will fetch the next row from the query into an object with elements named for the fields in the result.
|
||||
*/
|
||||
function Fetch() {
|
||||
return $this->sth->fetchObject();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will fetch the next row from the query into an array with numbered elements and with elements named
|
||||
* for the fields in the result.
|
||||
*/
|
||||
function FetchArray() {
|
||||
return $this->sth->fetch();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will fetch all result rows from the query into an array of objects with elements named for the fields in the result.
|
||||
*/
|
||||
function FetchAll() {
|
||||
return $this->sth->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An accessor for the number of rows affected when the query was executed.
|
||||
*/
|
||||
function Rows() {
|
||||
return $this->sth->rowCount();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to set the maximum duration for this query before it will be logged as a slow query.
|
||||
* @param double $seconds The maximum duration for this statement before logging it as 'slow'
|
||||
*/
|
||||
function MaxDuration( $seconds ) {
|
||||
$this->max_duration = $seconds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user