Added Kohana v3.0.9
This commit is contained in:
3
includes/kohana/modules/database/classes/database.php
Normal file
3
includes/kohana/modules/database/classes/database.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
abstract class Database extends Kohana_Database {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Exception extends Kohana_Database_Exception {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Expression extends Kohana_Database_Expression {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_MySQL extends Kohana_Database_MySQL {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_MySQL_Result extends Kohana_Database_MySQL_Result {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_PDO extends Kohana_Database_PDO {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Query extends Kohana_Database_Query {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
abstract class Database_Query_Builder extends Kohana_Database_Query_Builder {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Query_Builder_Delete extends Kohana_Database_Query_Builder_Delete {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Query_Builder_Insert extends Kohana_Database_Query_Builder_Insert {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Query_Builder_Join extends Kohana_Database_Query_Builder_Join {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Query_Builder_Select extends Kohana_Database_Query_Builder_Select {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Query_Builder_Update extends Kohana_Database_Query_Builder_Update {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
abstract class Database_Query_Builder_Where extends Kohana_Database_Query_Builder_Where {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
abstract class Database_Result extends Kohana_Database_Result {}
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Database_Result_Cached extends Kohana_Database_Result_Cached {}
|
3
includes/kohana/modules/database/classes/db.php
Normal file
3
includes/kohana/modules/database/classes/db.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class DB extends Kohana_DB {}
|
@@ -0,0 +1,97 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database-based configuration loader.
|
||||
*
|
||||
* Schema for configuration table:
|
||||
*
|
||||
* group_name varchar(128)
|
||||
* config_key varchar(128)
|
||||
* config_value text
|
||||
* primary key (group_name, config_key)
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Configuration
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Config_Database extends Kohana_Config_Reader {
|
||||
|
||||
protected $_database_instance = 'default';
|
||||
|
||||
protected $_database_table = 'config';
|
||||
|
||||
public function __construct(array $config = NULL)
|
||||
{
|
||||
if (isset($config['instance']))
|
||||
{
|
||||
$this->_database_instance = $config['instance'];
|
||||
}
|
||||
|
||||
if (isset($config['table']))
|
||||
{
|
||||
$this->_database_table = $config['table'];
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the configuration table for all values for this group and
|
||||
* unserialize each of the values.
|
||||
*
|
||||
* @param string group name
|
||||
* @param array configuration array
|
||||
* @return $this clone of the current object
|
||||
*/
|
||||
public function load($group, array $config = NULL)
|
||||
{
|
||||
if ($config === NULL AND $group !== 'database')
|
||||
{
|
||||
// Load all of the configuration values for this group
|
||||
$query = DB::select('config_key', 'config_value')
|
||||
->from($this->_database_table)
|
||||
->where('group_name', '=', $group)
|
||||
->execute($this->_database_instance);
|
||||
|
||||
if (count($query) > 0)
|
||||
{
|
||||
// Unserialize the configuration values
|
||||
$config = array_map('unserialize', $query->as_array('config_key', 'config_value'));
|
||||
}
|
||||
}
|
||||
|
||||
return parent::load($group, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload setting offsets to insert or update the database values as
|
||||
* changes occur.
|
||||
*
|
||||
* @param string array key
|
||||
* @param mixed new value
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
if ( ! $this->offsetExists($key))
|
||||
{
|
||||
// Insert a new value
|
||||
DB::insert($this->_database_table, array('group_name', 'config_key', 'config_value'))
|
||||
->values(array($this->_configuration_group, $key, serialize($value)))
|
||||
->execute($this->_database_instance);
|
||||
}
|
||||
elseif ($this->offsetGet($key) !== $value)
|
||||
{
|
||||
// Update the value
|
||||
DB::update($this->_database_table)
|
||||
->value('config_value', serialize($value))
|
||||
->where('group_name', '=', $this->_configuration_group)
|
||||
->where('config_key', '=', $key)
|
||||
->execute($this->_database_instance);
|
||||
}
|
||||
|
||||
return parent::offsetSet($key, $value);
|
||||
}
|
||||
|
||||
} // End Kohana_Config_Database
|
600
includes/kohana/modules/database/classes/kohana/database.php
Normal file
600
includes/kohana/modules/database/classes/kohana/database.php
Normal file
@@ -0,0 +1,600 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database connection wrapper/helper.
|
||||
*
|
||||
* You may get a database instance using `Database::instance('name')` where
|
||||
* name is the [config](database/config) group.
|
||||
*
|
||||
* This class provides connection instance management via Database Drivers, as
|
||||
* well as quoting, escaping and other related functions. Querys are done using
|
||||
* [Database_Query] and [Database_Query_Builder] objects, which can be easily
|
||||
* created using the [DB] helper class.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2010 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
abstract class Kohana_Database {
|
||||
|
||||
// Query types
|
||||
const SELECT = 1;
|
||||
const INSERT = 2;
|
||||
const UPDATE = 3;
|
||||
const DELETE = 4;
|
||||
|
||||
/**
|
||||
* @var string default instance name
|
||||
*/
|
||||
public static $default = 'default';
|
||||
|
||||
/**
|
||||
* @var array Database instances
|
||||
*/
|
||||
public static $instances = array();
|
||||
|
||||
/**
|
||||
* Get a singleton Database instance. If configuration is not specified,
|
||||
* it will be loaded from the database configuration file using the same
|
||||
* group as the name.
|
||||
*
|
||||
* // Load the default database
|
||||
* $db = Database::instance();
|
||||
*
|
||||
* // Create a custom configured instance
|
||||
* $db = Database::instance('custom', $config);
|
||||
*
|
||||
* @param string instance name
|
||||
* @param array configuration parameters
|
||||
* @return Database
|
||||
*/
|
||||
public static function instance($name = NULL, array $config = NULL)
|
||||
{
|
||||
if ($name === NULL)
|
||||
{
|
||||
// Use the default instance name
|
||||
$name = Database::$default;
|
||||
}
|
||||
|
||||
if ( ! isset(Database::$instances[$name]))
|
||||
{
|
||||
if ($config === NULL)
|
||||
{
|
||||
// Load the configuration for this database
|
||||
$config = Kohana::config('database')->$name;
|
||||
}
|
||||
|
||||
if ( ! isset($config['type']))
|
||||
{
|
||||
throw new Kohana_Exception('Database type not defined in :name configuration',
|
||||
array(':name' => $name));
|
||||
}
|
||||
|
||||
// Set the driver class name
|
||||
$driver = 'Database_'.ucfirst($config['type']);
|
||||
|
||||
// Create the database connection instance
|
||||
new $driver($name, $config);
|
||||
}
|
||||
|
||||
return Database::$instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string the last query executed
|
||||
*/
|
||||
public $last_query;
|
||||
|
||||
// Character that is used to quote identifiers
|
||||
protected $_identifier = '"';
|
||||
|
||||
// Instance name
|
||||
protected $_instance;
|
||||
|
||||
// Raw server connection
|
||||
protected $_connection;
|
||||
|
||||
// Configuration array
|
||||
protected $_config;
|
||||
|
||||
/**
|
||||
* Stores the database configuration locally and name the instance.
|
||||
*
|
||||
* [!!] This method cannot be accessed directly, you must use [Database::instance].
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function __construct($name, array $config)
|
||||
{
|
||||
// Set the instance name
|
||||
$this->_instance = $name;
|
||||
|
||||
// Store the config locally
|
||||
$this->_config = $config;
|
||||
|
||||
// Store the database instance
|
||||
Database::$instances[$name] = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the database when the object is destroyed.
|
||||
*
|
||||
* // Destroy the database instance
|
||||
* unset(Database::instances[(string) $db], $db);
|
||||
*
|
||||
* [!!] Calling `unset($db)` is not enough to destroy the database, as it
|
||||
* will still be stored in `Database::$instances`.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database instance name.
|
||||
*
|
||||
* echo (string) $db;
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public function __toString()
|
||||
{
|
||||
return $this->_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the database. This is called automatically when the first
|
||||
* query is executed.
|
||||
*
|
||||
* $db->connect();
|
||||
*
|
||||
* @throws Database_Exception
|
||||
* @return void
|
||||
*/
|
||||
abstract public function connect();
|
||||
|
||||
/**
|
||||
* Disconnect from the database. This is called automatically by [Database::__destruct].
|
||||
*
|
||||
* $db->disconnect();
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function disconnect();
|
||||
|
||||
/**
|
||||
* Set the connection character set. This is called automatically by [Database::connect].
|
||||
*
|
||||
* $db->set_charset('utf8');
|
||||
*
|
||||
* @throws Database_Exception
|
||||
* @param string character set name
|
||||
* @return void
|
||||
*/
|
||||
abstract public function set_charset($charset);
|
||||
|
||||
/**
|
||||
* Perform an SQL query of the given type.
|
||||
*
|
||||
* // Make a SELECT query and use objects for results
|
||||
* $db->query(Database::SELECT, 'SELECT * FROM groups', TRUE);
|
||||
*
|
||||
* // Make a SELECT query and use "Model_User" for the results
|
||||
* $db->query(Database::SELECT, 'SELECT * FROM users LIMIT 1', 'Model_User');
|
||||
*
|
||||
* @param integer Database::SELECT, Database::INSERT, etc
|
||||
* @param string SQL query
|
||||
* @param mixed result object class string, TRUE for stdClass, FALSE for assoc array
|
||||
* @param array object construct parameters for result class
|
||||
* @return object Database_Result for SELECT queries
|
||||
* @return array list (insert id, row count) for INSERT queries
|
||||
* @return integer number of affected rows for all other queries
|
||||
*/
|
||||
abstract public function query($type, $sql, $as_object = FALSE, array $params = NULL);
|
||||
|
||||
/**
|
||||
* Count the number of records in the last query, without LIMIT or OFFSET applied.
|
||||
*
|
||||
* // Get the total number of records that match the last query
|
||||
* $count = $db->count_last_query();
|
||||
*
|
||||
* @deprecated since v3.0.9
|
||||
* @return integer
|
||||
*/
|
||||
public function count_last_query()
|
||||
{
|
||||
if ($sql = $this->last_query)
|
||||
{
|
||||
$sql = trim($sql);
|
||||
if (stripos($sql, 'SELECT') !== 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (stripos($sql, 'LIMIT') !== FALSE)
|
||||
{
|
||||
// Remove LIMIT from the SQL
|
||||
$sql = preg_replace('/\sLIMIT\s+[^a-z]+/i', ' ', $sql);
|
||||
}
|
||||
|
||||
if (stripos($sql, 'OFFSET') !== FALSE)
|
||||
{
|
||||
// Remove OFFSET from the SQL
|
||||
$sql = preg_replace('/\sOFFSET\s+\d+/i', '', $sql);
|
||||
}
|
||||
|
||||
// Get the total rows from the last query executed
|
||||
$result = $this->query
|
||||
(
|
||||
Database::SELECT,
|
||||
'SELECT COUNT(*) AS '.$this->quote_identifier('total_rows').' '
|
||||
.'FROM ('.$sql.') AS '.$this->quote_table('counted_results'),
|
||||
TRUE
|
||||
);
|
||||
|
||||
// Return the total number of rows from the query
|
||||
return (int) $result->current()->total_rows;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of records in a table.
|
||||
*
|
||||
* // Get the total number of records in the "users" table
|
||||
* $count = $db->count_records('users');
|
||||
*
|
||||
* @param mixed table name string or array(query, alias)
|
||||
* @return integer
|
||||
*/
|
||||
public function count_records($table)
|
||||
{
|
||||
// Quote the table name
|
||||
$table = $this->quote_identifier($table);
|
||||
|
||||
return $this->query(Database::SELECT, 'SELECT COUNT(*) AS total_row_count FROM '.$table, FALSE)
|
||||
->get('total_row_count');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized array describing the SQL data type
|
||||
*
|
||||
* $db->datatype('char');
|
||||
*
|
||||
* @param string SQL data type
|
||||
* @return array
|
||||
*/
|
||||
public function datatype($type)
|
||||
{
|
||||
static $types = array
|
||||
(
|
||||
// SQL-92
|
||||
'bit' => array('type' => 'string', 'exact' => TRUE),
|
||||
'bit varying' => array('type' => 'string'),
|
||||
'char' => array('type' => 'string', 'exact' => TRUE),
|
||||
'char varying' => array('type' => 'string'),
|
||||
'character' => array('type' => 'string', 'exact' => TRUE),
|
||||
'character varying' => array('type' => 'string'),
|
||||
'date' => array('type' => 'string'),
|
||||
'dec' => array('type' => 'float', 'exact' => TRUE),
|
||||
'decimal' => array('type' => 'float', 'exact' => TRUE),
|
||||
'double precision' => array('type' => 'float'),
|
||||
'float' => array('type' => 'float'),
|
||||
'int' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'),
|
||||
'integer' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'),
|
||||
'interval' => array('type' => 'string'),
|
||||
'national char' => array('type' => 'string', 'exact' => TRUE),
|
||||
'national char varying' => array('type' => 'string'),
|
||||
'national character' => array('type' => 'string', 'exact' => TRUE),
|
||||
'national character varying' => array('type' => 'string'),
|
||||
'nchar' => array('type' => 'string', 'exact' => TRUE),
|
||||
'nchar varying' => array('type' => 'string'),
|
||||
'numeric' => array('type' => 'float', 'exact' => TRUE),
|
||||
'real' => array('type' => 'float'),
|
||||
'smallint' => array('type' => 'int', 'min' => '-32768', 'max' => '32767'),
|
||||
'time' => array('type' => 'string'),
|
||||
'time with time zone' => array('type' => 'string'),
|
||||
'timestamp' => array('type' => 'string'),
|
||||
'timestamp with time zone' => array('type' => 'string'),
|
||||
'varchar' => array('type' => 'string'),
|
||||
|
||||
// SQL:1999
|
||||
'binary large object' => array('type' => 'string', 'binary' => TRUE),
|
||||
'blob' => array('type' => 'string', 'binary' => TRUE),
|
||||
'boolean' => array('type' => 'bool'),
|
||||
'char large object' => array('type' => 'string'),
|
||||
'character large object' => array('type' => 'string'),
|
||||
'clob' => array('type' => 'string'),
|
||||
'national character large object' => array('type' => 'string'),
|
||||
'nchar large object' => array('type' => 'string'),
|
||||
'nclob' => array('type' => 'string'),
|
||||
'time without time zone' => array('type' => 'string'),
|
||||
'timestamp without time zone' => array('type' => 'string'),
|
||||
|
||||
// SQL:2003
|
||||
'bigint' => array('type' => 'int', 'min' => '-9223372036854775808', 'max' => '9223372036854775807'),
|
||||
|
||||
// SQL:2008
|
||||
'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE),
|
||||
'binary varying' => array('type' => 'string', 'binary' => TRUE),
|
||||
'varbinary' => array('type' => 'string', 'binary' => TRUE),
|
||||
);
|
||||
|
||||
if (isset($types[$type]))
|
||||
return $types[$type];
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all of the tables in the database. Optionally, a LIKE string can
|
||||
* be used to search for specific tables.
|
||||
*
|
||||
* // Get all tables in the current database
|
||||
* $tables = $db->list_tables();
|
||||
*
|
||||
* // Get all user-related tables
|
||||
* $tables = $db->list_tables('user%');
|
||||
*
|
||||
* @param string table to search for
|
||||
* @return array
|
||||
*/
|
||||
abstract public function list_tables($like = NULL);
|
||||
|
||||
/**
|
||||
* Lists all of the columns in a table. Optionally, a LIKE string can be
|
||||
* used to search for specific fields.
|
||||
*
|
||||
* // Get all columns from the "users" table
|
||||
* $columns = $db->list_columns('users');
|
||||
*
|
||||
* // Get all name-related columns
|
||||
* $columns = $db->list_columns('users', '%name%');
|
||||
*
|
||||
* // Get the columns from a table that doesn't use the table prefix
|
||||
* $columns = $db->list_columns('users', NULL, FALSE);
|
||||
*
|
||||
* @param string table to get columns from
|
||||
* @param string column to search for
|
||||
* @param boolean whether to add the table prefix automatically or not
|
||||
* @return array
|
||||
*/
|
||||
abstract public function list_columns($table, $like = NULL, $add_prefix = TRUE);
|
||||
|
||||
/**
|
||||
* Extracts the text between parentheses, if any.
|
||||
*
|
||||
* // Returns: array('CHAR', '6')
|
||||
* list($type, $length) = $db->_parse_type('CHAR(6)');
|
||||
*
|
||||
* @param string
|
||||
* @return array list containing the type and length, if any
|
||||
*/
|
||||
protected function _parse_type($type)
|
||||
{
|
||||
if (($open = strpos($type, '(')) === FALSE)
|
||||
{
|
||||
// No length specified
|
||||
return array($type, NULL);
|
||||
}
|
||||
|
||||
// Closing parenthesis
|
||||
$close = strpos($type, ')', $open);
|
||||
|
||||
// Length without parentheses
|
||||
$length = substr($type, $open + 1, $close - 1 - $open);
|
||||
|
||||
// Type without the length
|
||||
$type = substr($type, 0, $open).substr($type, $close + 1);
|
||||
|
||||
return array($type, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table prefix defined in the current configuration.
|
||||
*
|
||||
* $prefix = $db->table_prefix();
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function table_prefix()
|
||||
{
|
||||
return $this->_config['table_prefix'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a value for an SQL query.
|
||||
*
|
||||
* $db->quote(NULL); // 'NULL'
|
||||
* $db->quote(10); // 10
|
||||
* $db->quote('fred'); // 'fred'
|
||||
*
|
||||
* Objects passed to this function will be converted to strings.
|
||||
* [Database_Expression] objects will use the value of the expression.
|
||||
* [Database_Query] objects will be compiled and converted to a sub-query.
|
||||
* All other objects will be converted using the `__toString` method.
|
||||
*
|
||||
* @param mixed any value to quote
|
||||
* @return string
|
||||
* @uses Database::escape
|
||||
*/
|
||||
public function quote($value)
|
||||
{
|
||||
if ($value === NULL)
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
elseif ($value === TRUE)
|
||||
{
|
||||
return "'1'";
|
||||
}
|
||||
elseif ($value === FALSE)
|
||||
{
|
||||
return "'0'";
|
||||
}
|
||||
elseif (is_object($value))
|
||||
{
|
||||
if ($value instanceof Database_Query)
|
||||
{
|
||||
// Create a sub-query
|
||||
return '('.$value->compile($this).')';
|
||||
}
|
||||
elseif ($value instanceof Database_Expression)
|
||||
{
|
||||
// Use a raw expression
|
||||
return $value->value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the object to a string
|
||||
return $this->quote( (string) $value);
|
||||
}
|
||||
}
|
||||
elseif (is_array($value))
|
||||
{
|
||||
return '('.implode(', ', array_map(array($this, __FUNCTION__), $value)).')';
|
||||
}
|
||||
elseif (is_int($value))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
elseif (is_float($value))
|
||||
{
|
||||
// Convert to non-locale aware float to prevent possible commas
|
||||
return sprintf('%F', $value);
|
||||
}
|
||||
|
||||
return $this->escape($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a database table name and adds the table prefix if needed.
|
||||
*
|
||||
* $table = $db->quote_table($table);
|
||||
*
|
||||
* @param mixed table name or array(table, alias)
|
||||
* @return string
|
||||
* @uses Database::quote_identifier
|
||||
* @uses Database::table_prefix
|
||||
*/
|
||||
public function quote_table($value)
|
||||
{
|
||||
// Assign the table by reference from the value
|
||||
if (is_array($value))
|
||||
{
|
||||
$table =& $value[0];
|
||||
|
||||
// Attach table prefix to alias
|
||||
$value[1] = $this->table_prefix().$value[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$table =& $value;
|
||||
}
|
||||
|
||||
if (is_string($table) AND strpos($table, '.') === FALSE)
|
||||
{
|
||||
// Add the table prefix for tables
|
||||
$table = $this->table_prefix().$table;
|
||||
}
|
||||
|
||||
return $this->quote_identifier($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a database identifier, such as a column name. Adds the
|
||||
* table prefix to the identifier if a table name is present.
|
||||
*
|
||||
* $column = $db->quote_identifier($column);
|
||||
*
|
||||
* You can also use SQL methods within identifiers.
|
||||
*
|
||||
* // The value of "column" will be quoted
|
||||
* $column = $db->quote_identifier('COUNT("column")');
|
||||
*
|
||||
* Objects passed to this function will be converted to strings.
|
||||
* [Database_Expression] objects will use the value of the expression.
|
||||
* [Database_Query] objects will be compiled and converted to a sub-query.
|
||||
* All other objects will be converted using the `__toString` method.
|
||||
*
|
||||
* @param mixed any identifier
|
||||
* @return string
|
||||
* @uses Database::table_prefix
|
||||
*/
|
||||
public function quote_identifier($value)
|
||||
{
|
||||
if ($value === '*')
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
elseif (is_object($value))
|
||||
{
|
||||
if ($value instanceof Database_Query)
|
||||
{
|
||||
// Create a sub-query
|
||||
return '('.$value->compile($this).')';
|
||||
}
|
||||
elseif ($value instanceof Database_Expression)
|
||||
{
|
||||
// Use a raw expression
|
||||
return $value->value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the object to a string
|
||||
return $this->quote_identifier( (string) $value);
|
||||
}
|
||||
}
|
||||
elseif (is_array($value))
|
||||
{
|
||||
// Separate the column and alias
|
||||
list ($value, $alias) = $value;
|
||||
|
||||
return $this->quote_identifier($value).' AS '.$this->quote_identifier($alias);
|
||||
}
|
||||
|
||||
if (strpos($value, '"') !== FALSE)
|
||||
{
|
||||
// Quote the column in FUNC("ident") identifiers
|
||||
return preg_replace('/"(.+?)"/e', '$this->quote_identifier("$1")', $value);
|
||||
}
|
||||
elseif (strpos($value, '.') !== FALSE)
|
||||
{
|
||||
// Split the identifier into the individual parts
|
||||
$parts = explode('.', $value);
|
||||
|
||||
if ($prefix = $this->table_prefix())
|
||||
{
|
||||
// Get the offset of the table name, 2nd-to-last part
|
||||
// This works for databases that can have 3 identifiers (Postgre)
|
||||
$offset = count($parts) - 2;
|
||||
|
||||
// Add the table prefix to the table name
|
||||
$parts[$offset] = $prefix.$parts[$offset];
|
||||
}
|
||||
|
||||
// Quote each of the parts
|
||||
return implode('.', array_map(array($this, __FUNCTION__), $parts));
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->_identifier.$value.$this->_identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a string by escaping characters that could cause an SQL
|
||||
* injection attack.
|
||||
*
|
||||
* $value = $db->escape('any string');
|
||||
*
|
||||
* @param string value to quote
|
||||
* @return string
|
||||
*/
|
||||
abstract public function escape($value);
|
||||
|
||||
} // End Database_Connection
|
@@ -0,0 +1,11 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database exceptions.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Exceptions
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Exception extends Kohana_Exception {}
|
@@ -0,0 +1,62 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database expressions can be used to add unescaped SQL fragments to a
|
||||
* [Database_Query_Builder] object.
|
||||
*
|
||||
* For example, you can use an expression to generate a column alias:
|
||||
*
|
||||
* // SELECT CONCAT(first_name, last_name) AS full_name
|
||||
* $query = DB::select(array(DB::expr('CONCAT(first_name, last_name)'), 'full_name')));
|
||||
*
|
||||
* More examples are available on the [Query Builder](database/query/builder#database-expressions) page
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Expression {
|
||||
|
||||
// Raw expression string
|
||||
protected $_value;
|
||||
|
||||
/**
|
||||
* Sets the expression string.
|
||||
*
|
||||
* $expression = new Database_Expression('COUNT(users.id)');
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
// Set the expression string
|
||||
$this->_value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expression value as a string.
|
||||
*
|
||||
* $sql = $expression->value();
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function value()
|
||||
{
|
||||
return (string) $this->_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the expression as a string.
|
||||
*
|
||||
* echo $expression;
|
||||
*
|
||||
* @return string
|
||||
* @uses Database_Expression::value
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->value();
|
||||
}
|
||||
|
||||
} // End Database_Expression
|
@@ -0,0 +1,379 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* MySQL database connection.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Drivers
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_MySQL extends Database {
|
||||
|
||||
// Database in use by each connection
|
||||
protected static $_current_databases = array();
|
||||
|
||||
// Use SET NAMES to set the character set
|
||||
protected static $_set_names;
|
||||
|
||||
// Identifier for this connection within the PHP driver
|
||||
protected $_connection_id;
|
||||
|
||||
// MySQL uses a backtick for identifiers
|
||||
protected $_identifier = '`';
|
||||
|
||||
public function connect()
|
||||
{
|
||||
if ($this->_connection)
|
||||
return;
|
||||
|
||||
if (Database_MySQL::$_set_names === NULL)
|
||||
{
|
||||
// Determine if we can use mysql_set_charset(), which is only
|
||||
// available on PHP 5.2.3+ when compiled against MySQL 5.0+
|
||||
Database_MySQL::$_set_names = ! function_exists('mysql_set_charset');
|
||||
}
|
||||
|
||||
// Extract the connection parameters, adding required variabels
|
||||
extract($this->_config['connection'] + array(
|
||||
'database' => '',
|
||||
'hostname' => '',
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
'persistent' => FALSE,
|
||||
));
|
||||
|
||||
// Prevent this information from showing up in traces
|
||||
unset($this->_config['connection']['username'], $this->_config['connection']['password']);
|
||||
|
||||
try
|
||||
{
|
||||
if ($persistent)
|
||||
{
|
||||
// Create a persistent connection
|
||||
$this->_connection = mysql_pconnect($hostname, $username, $password);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a connection and force it to be a new link
|
||||
$this->_connection = mysql_connect($hostname, $username, $password, TRUE);
|
||||
}
|
||||
}
|
||||
catch (ErrorException $e)
|
||||
{
|
||||
// No connection exists
|
||||
$this->_connection = NULL;
|
||||
|
||||
throw new Database_Exception(':error', array(
|
||||
':error' => mysql_error(),
|
||||
),
|
||||
mysql_errno());
|
||||
}
|
||||
|
||||
// \xFF is a better delimiter, but the PHP driver uses underscore
|
||||
$this->_connection_id = sha1($hostname.'_'.$username.'_'.$password);
|
||||
|
||||
$this->_select_db($database);
|
||||
|
||||
if ( ! empty($this->_config['charset']))
|
||||
{
|
||||
// Set the character set
|
||||
$this->set_charset($this->_config['charset']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the database
|
||||
*
|
||||
* @param string Database
|
||||
* @return void
|
||||
*/
|
||||
protected function _select_db($database)
|
||||
{
|
||||
if ( ! mysql_select_db($database, $this->_connection))
|
||||
{
|
||||
// Unable to select database
|
||||
throw new Database_Exception(':error',
|
||||
array(':error' => mysql_error($this->_connection)),
|
||||
mysql_errno($this->_connection));
|
||||
}
|
||||
|
||||
Database_MySQL::$_current_databases[$this->_connection_id] = $database;
|
||||
}
|
||||
|
||||
public function disconnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Database is assumed disconnected
|
||||
$status = TRUE;
|
||||
|
||||
if (is_resource($this->_connection))
|
||||
{
|
||||
if ($status = mysql_close($this->_connection))
|
||||
{
|
||||
// Clear the connection
|
||||
$this->_connection = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// Database is probably not disconnected
|
||||
$status = ! is_resource($this->_connection);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function set_charset($charset)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if (Database_MySQL::$_set_names === TRUE)
|
||||
{
|
||||
// PHP is compiled against MySQL 4.x
|
||||
$status = (bool) mysql_query('SET NAMES '.$this->quote($charset), $this->_connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PHP is compiled against MySQL 5.x
|
||||
$status = mysql_set_charset($charset, $this->_connection);
|
||||
}
|
||||
|
||||
if ($status === FALSE)
|
||||
{
|
||||
throw new Database_Exception(':error',
|
||||
array(':error' => mysql_error($this->_connection)),
|
||||
mysql_errno($this->_connection));
|
||||
}
|
||||
}
|
||||
|
||||
public function query($type, $sql, $as_object = FALSE, array $params = NULL)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if ( ! empty($this->_config['profiling']))
|
||||
{
|
||||
// Benchmark this query for the current instance
|
||||
$benchmark = Profiler::start("Database ({$this->_instance})", $sql);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_config['connection']['persistent']) AND $this->_config['connection']['database'] !== Database_MySQL::$_current_databases[$this->_connection_id])
|
||||
{
|
||||
// Select database on persistent connections
|
||||
$this->_select_db($this->_config['connection']['database']);
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
if (($result = mysql_query($sql, $this->_connection)) === FALSE)
|
||||
{
|
||||
if (isset($benchmark))
|
||||
{
|
||||
// This benchmark is worthless
|
||||
Profiler::delete($benchmark);
|
||||
}
|
||||
|
||||
throw new Database_Exception(':error [ :query ]',
|
||||
array(':error' => mysql_error($this->_connection), ':query' => $sql),
|
||||
mysql_errno($this->_connection));
|
||||
}
|
||||
|
||||
if (isset($benchmark))
|
||||
{
|
||||
Profiler::stop($benchmark);
|
||||
}
|
||||
|
||||
// Set the last query
|
||||
$this->last_query = $sql;
|
||||
|
||||
if ($type === Database::SELECT)
|
||||
{
|
||||
// Return an iterator of results
|
||||
return new Database_MySQL_Result($result, $sql, $as_object, $params);
|
||||
}
|
||||
elseif ($type === Database::INSERT)
|
||||
{
|
||||
// Return a list of insert id and rows created
|
||||
return array(
|
||||
mysql_insert_id($this->_connection),
|
||||
mysql_affected_rows($this->_connection),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the number of rows affected
|
||||
return mysql_affected_rows($this->_connection);
|
||||
}
|
||||
}
|
||||
|
||||
public function datatype($type)
|
||||
{
|
||||
static $types = array
|
||||
(
|
||||
'blob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '65535'),
|
||||
'bool' => array('type' => 'bool'),
|
||||
'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'),
|
||||
'datetime' => array('type' => 'string'),
|
||||
'decimal unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
|
||||
'double' => array('type' => 'float'),
|
||||
'double precision unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'double unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'enum' => array('type' => 'string'),
|
||||
'fixed' => array('type' => 'float', 'exact' => TRUE),
|
||||
'fixed unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
|
||||
'float unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
|
||||
'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
|
||||
'longblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '4294967295'),
|
||||
'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'),
|
||||
'mediumblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '16777215'),
|
||||
'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'),
|
||||
'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'),
|
||||
'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'),
|
||||
'national varchar' => array('type' => 'string'),
|
||||
'numeric unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
|
||||
'nvarchar' => array('type' => 'string'),
|
||||
'point' => array('type' => 'string', 'binary' => TRUE),
|
||||
'real unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'set' => array('type' => 'string'),
|
||||
'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'),
|
||||
'text' => array('type' => 'string', 'character_maximum_length' => '65535'),
|
||||
'tinyblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '255'),
|
||||
'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'),
|
||||
'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'),
|
||||
'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'),
|
||||
'year' => array('type' => 'string'),
|
||||
);
|
||||
|
||||
$type = str_replace(' zerofill', '', $type);
|
||||
|
||||
if (isset($types[$type]))
|
||||
return $types[$type];
|
||||
|
||||
return parent::datatype($type);
|
||||
}
|
||||
|
||||
public function list_tables($like = NULL)
|
||||
{
|
||||
if (is_string($like))
|
||||
{
|
||||
// Search for table names
|
||||
$result = $this->query(Database::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find all table names
|
||||
$result = $this->query(Database::SELECT, 'SHOW TABLES', FALSE);
|
||||
}
|
||||
|
||||
$tables = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
$tables[] = reset($row);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
public function list_columns($table, $like = NULL, $add_prefix = TRUE)
|
||||
{
|
||||
// Quote the table name
|
||||
$table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table;
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
// Search for column names
|
||||
$result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find all column names
|
||||
$result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$columns = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
list($type, $length) = $this->_parse_type($row['Type']);
|
||||
|
||||
$column = $this->datatype($type);
|
||||
|
||||
$column['column_name'] = $row['Field'];
|
||||
$column['column_default'] = $row['Default'];
|
||||
$column['data_type'] = $type;
|
||||
$column['is_nullable'] = ($row['Null'] == 'YES');
|
||||
$column['ordinal_position'] = ++$count;
|
||||
|
||||
switch ($column['type'])
|
||||
{
|
||||
case 'float':
|
||||
if (isset($length))
|
||||
{
|
||||
list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
|
||||
}
|
||||
break;
|
||||
case 'int':
|
||||
if (isset($length))
|
||||
{
|
||||
// MySQL attribute
|
||||
$column['display'] = $length;
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
switch ($column['data_type'])
|
||||
{
|
||||
case 'binary':
|
||||
case 'varbinary':
|
||||
$column['character_maximum_length'] = $length;
|
||||
break;
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
$column['character_maximum_length'] = $length;
|
||||
case 'text':
|
||||
case 'tinytext':
|
||||
case 'mediumtext':
|
||||
case 'longtext':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
break;
|
||||
case 'enum':
|
||||
case 'set':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
$column['options'] = explode('\',\'', substr($length, 1, -1));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// MySQL attributes
|
||||
$column['comment'] = $row['Comment'];
|
||||
$column['extra'] = $row['Extra'];
|
||||
$column['key'] = $row['Key'];
|
||||
$column['privileges'] = $row['Privileges'];
|
||||
|
||||
$columns[$row['Field']] = $column;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function escape($value)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if (($value = mysql_real_escape_string( (string) $value, $this->_connection)) === FALSE)
|
||||
{
|
||||
throw new Database_Exception(':error',
|
||||
array(':error' => mysql_errno($this->_connection)),
|
||||
mysql_error($this->_connection));
|
||||
}
|
||||
|
||||
// SQL standard is to use single-quotes for all values
|
||||
return "'$value'";
|
||||
}
|
||||
|
||||
} // End Database_MySQL
|
@@ -0,0 +1,71 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* MySQL database result. See [Results](/database/results) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query/Result
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_MySQL_Result extends Database_Result {
|
||||
|
||||
protected $_internal_row = 0;
|
||||
|
||||
public function __construct($result, $sql, $as_object = FALSE, array $params = NULL)
|
||||
{
|
||||
parent::__construct($result, $sql, $as_object, $params);
|
||||
|
||||
// Find the number of rows in the result
|
||||
$this->_total_rows = mysql_num_rows($result);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (is_resource($this->_result))
|
||||
{
|
||||
mysql_free_result($this->_result);
|
||||
}
|
||||
}
|
||||
|
||||
public function seek($offset)
|
||||
{
|
||||
if ($this->offsetExists($offset) AND mysql_data_seek($this->_result, $offset))
|
||||
{
|
||||
// Set the current row to the offset
|
||||
$this->_current_row = $this->_internal_row = $offset;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
if ($this->_current_row !== $this->_internal_row AND ! $this->seek($this->_current_row))
|
||||
return FALSE;
|
||||
|
||||
// Increment internal row for optimization assuming rows are fetched in order
|
||||
$this->_internal_row++;
|
||||
|
||||
if ($this->_as_object === TRUE)
|
||||
{
|
||||
// Return an stdClass
|
||||
return mysql_fetch_object($this->_result);
|
||||
}
|
||||
elseif (is_string($this->_as_object))
|
||||
{
|
||||
// Return an object of given class name
|
||||
return mysql_fetch_object($this->_result, $this->_as_object, $this->_object_params);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return an array of the row
|
||||
return mysql_fetch_assoc($this->_result);
|
||||
}
|
||||
}
|
||||
|
||||
} // End Database_MySQL_Result_Select
|
186
includes/kohana/modules/database/classes/kohana/database/pdo.php
Normal file
186
includes/kohana/modules/database/classes/kohana/database/pdo.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* PDO database connection.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Drivers
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_PDO extends Database {
|
||||
|
||||
// PDO uses no quoting for identifiers
|
||||
protected $_identifier = '';
|
||||
|
||||
protected function __construct($name, array $config)
|
||||
{
|
||||
parent::__construct($name, $config);
|
||||
|
||||
if (isset($this->_config['identifier']))
|
||||
{
|
||||
// Allow the identifier to be overloaded per-connection
|
||||
$this->_identifier = (string) $this->_config['identifier'];
|
||||
}
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
if ($this->_connection)
|
||||
return;
|
||||
|
||||
// Extract the connection parameters, adding required variabels
|
||||
extract($this->_config['connection'] + array(
|
||||
'dsn' => '',
|
||||
'username' => NULL,
|
||||
'password' => NULL,
|
||||
'persistent' => FALSE,
|
||||
));
|
||||
|
||||
// Clear the connection parameters for security
|
||||
unset($this->_config['connection']);
|
||||
|
||||
// Force PDO to use exceptions for all errors
|
||||
$attrs = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
if ( ! empty($persistent))
|
||||
{
|
||||
// Make the connection persistent
|
||||
$attrs[PDO::ATTR_PERSISTENT] = TRUE;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create a new PDO connection
|
||||
$this->_connection = new PDO($dsn, $username, $password, $attrs);
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new Database_Exception(':error', array(
|
||||
':error' => $e->getMessage(),
|
||||
),
|
||||
$e->getCode(),
|
||||
$e);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_config['charset']))
|
||||
{
|
||||
// Set the character set
|
||||
$this->set_charset($this->_config['charset']);
|
||||
}
|
||||
}
|
||||
|
||||
public function disconnect()
|
||||
{
|
||||
// Destroy the PDO object
|
||||
$this->_connection = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function set_charset($charset)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
// Execute a raw SET NAMES query
|
||||
$this->_connection->exec('SET NAMES '.$this->quote($charset));
|
||||
}
|
||||
|
||||
public function query($type, $sql, $as_object = FALSE, array $params = NULL)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if ( ! empty($this->_config['profiling']))
|
||||
{
|
||||
// Benchmark this query for the current instance
|
||||
$benchmark = Profiler::start("Database ({$this->_instance})", $sql);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$result = $this->_connection->query($sql);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
if (isset($benchmark))
|
||||
{
|
||||
// This benchmark is worthless
|
||||
Profiler::delete($benchmark);
|
||||
}
|
||||
|
||||
// Convert the exception in a database exception
|
||||
throw new Database_Exception(':error [ :query ]', array(
|
||||
':error' => $e->getMessage(),
|
||||
':query' => $sql
|
||||
),
|
||||
$e->getCode(),
|
||||
$e);
|
||||
}
|
||||
|
||||
if (isset($benchmark))
|
||||
{
|
||||
Profiler::stop($benchmark);
|
||||
}
|
||||
|
||||
// Set the last query
|
||||
$this->last_query = $sql;
|
||||
|
||||
if ($type === Database::SELECT)
|
||||
{
|
||||
// Convert the result into an array, as PDOStatement::rowCount is not reliable
|
||||
if ($as_object === FALSE)
|
||||
{
|
||||
$result->setFetchMode(PDO::FETCH_ASSOC);
|
||||
}
|
||||
elseif (is_string($as_object))
|
||||
{
|
||||
$result->setFetchMode(PDO::FETCH_CLASS, $as_object, $params);
|
||||
}
|
||||
else
|
||||
{
|
||||
$result->setFetchMode(PDO::FETCH_CLASS, 'stdClass');
|
||||
}
|
||||
|
||||
$result = $result->fetchAll();
|
||||
|
||||
// Return an iterator of results
|
||||
return new Database_Result_Cached($result, $sql, $as_object, $params);
|
||||
}
|
||||
elseif ($type === Database::INSERT)
|
||||
{
|
||||
// Return a list of insert id and rows created
|
||||
return array(
|
||||
$this->_connection->lastInsertId(),
|
||||
$result->rowCount(),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the number of rows affected
|
||||
return $result->rowCount();
|
||||
}
|
||||
}
|
||||
|
||||
public function list_tables($like = NULL)
|
||||
{
|
||||
throw new Kohana_Exception('Database method :method is not supported by :class',
|
||||
array(':method' => __FUNCTION__, ':class' => __CLASS__));
|
||||
}
|
||||
|
||||
public function list_columns($table, $like = NULL, $add_prefix = TRUE)
|
||||
{
|
||||
throw new Kohana_Exception('Database method :method is not supported by :class',
|
||||
array(':method' => __FUNCTION__, ':class' => __CLASS__));
|
||||
}
|
||||
|
||||
public function escape($value)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
return $this->_connection->quote($value);
|
||||
}
|
||||
|
||||
} // End Database_PDO
|
@@ -0,0 +1,236 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query wrapper. See [Prepared Statements](database/query/prepared) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Query {
|
||||
|
||||
// Query type
|
||||
protected $_type;
|
||||
|
||||
// Cache lifetime
|
||||
protected $_lifetime;
|
||||
|
||||
// SQL statement
|
||||
protected $_sql;
|
||||
|
||||
// Quoted query parameters
|
||||
protected $_parameters = array();
|
||||
|
||||
// Return results as associative arrays or objects
|
||||
protected $_as_object = FALSE;
|
||||
|
||||
// Parameters for __construct when using object results
|
||||
protected $_object_params = array();
|
||||
|
||||
/**
|
||||
* Creates a new SQL query of the specified type.
|
||||
*
|
||||
* @param integer query type: Database::SELECT, Database::INSERT, etc
|
||||
* @param string query string
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($type, $sql)
|
||||
{
|
||||
$this->_type = $type;
|
||||
$this->_sql = $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL query string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public function __toString()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Return the SQL string
|
||||
return $this->compile(Database::instance());
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return Kohana::exception_text($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return $this->_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the query to be cached for a specified amount of time.
|
||||
*
|
||||
* @param integer number of seconds to cache or null for default
|
||||
* @return $this
|
||||
* @uses Kohana::$cache_life
|
||||
*/
|
||||
public function cached($lifetime = NULL)
|
||||
{
|
||||
if ($lifetime === NULL)
|
||||
{
|
||||
// Use the global setting
|
||||
$lifetime = Kohana::$cache_life;
|
||||
}
|
||||
|
||||
$this->_lifetime = $lifetime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns results as associative arrays
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function as_assoc()
|
||||
{
|
||||
$this->_as_object = FALSE;
|
||||
|
||||
$this->_object_params = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns results as objects
|
||||
*
|
||||
* @param string classname or TRUE for stdClass
|
||||
* @return $this
|
||||
*/
|
||||
public function as_object($class = TRUE, array $params = NULL)
|
||||
{
|
||||
$this->_as_object = $class;
|
||||
|
||||
if ($params)
|
||||
{
|
||||
// Add object parameters
|
||||
$this->_object_params = $params;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a parameter in the query.
|
||||
*
|
||||
* @param string parameter key to replace
|
||||
* @param mixed value to use
|
||||
* @return $this
|
||||
*/
|
||||
public function param($param, $value)
|
||||
{
|
||||
// Add or overload a new parameter
|
||||
$this->_parameters[$param] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a variable to a parameter in the query.
|
||||
*
|
||||
* @param string parameter key to replace
|
||||
* @param mixed variable to use
|
||||
* @return $this
|
||||
*/
|
||||
public function bind($param, & $var)
|
||||
{
|
||||
// Bind a value to a variable
|
||||
$this->_parameters[$param] =& $var;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple parameters to the query.
|
||||
*
|
||||
* @param array list of parameters
|
||||
* @return $this
|
||||
*/
|
||||
public function parameters(array $params)
|
||||
{
|
||||
// Merge the new parameters in
|
||||
$this->_parameters = $params + $this->_parameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it. Replaces any parameters with their
|
||||
* given values.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @return string
|
||||
*/
|
||||
public function compile(Database $db)
|
||||
{
|
||||
// Import the SQL locally
|
||||
$sql = $this->_sql;
|
||||
|
||||
if ( ! empty($this->_parameters))
|
||||
{
|
||||
// Quote all of the values
|
||||
$values = array_map(array($db, 'quote'), $this->_parameters);
|
||||
|
||||
// Replace the values in the SQL
|
||||
$sql = strtr($sql, $values);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the current query on the given database.
|
||||
*
|
||||
* @param mixed Database instance or name of instance
|
||||
* @return object Database_Result for SELECT queries
|
||||
* @return mixed the insert id for INSERT queries
|
||||
* @return integer number of affected rows for all other queries
|
||||
*/
|
||||
public function execute($db = NULL)
|
||||
{
|
||||
if ( ! is_object($db))
|
||||
{
|
||||
// Get the database instance
|
||||
$db = Database::instance($db);
|
||||
}
|
||||
|
||||
// Compile the SQL query
|
||||
$sql = $this->compile($db);
|
||||
|
||||
if ( ! empty($this->_lifetime) AND $this->_type === Database::SELECT)
|
||||
{
|
||||
// Set the cache key based on the database instance name and SQL
|
||||
$cache_key = 'Database::query("'.$db.'", "'.$sql.'")';
|
||||
|
||||
if ($result = Kohana::cache($cache_key, NULL, $this->_lifetime))
|
||||
{
|
||||
// Return a cached result
|
||||
return new Database_Result_Cached($result, $sql, $this->_as_object, $this->_object_params);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
$result = $db->query($this->_type, $sql, $this->_as_object, $this->_object_params);
|
||||
|
||||
if (isset($cache_key))
|
||||
{
|
||||
// Cache the result array
|
||||
Kohana::cache($cache_key, $result->as_array(), $this->_lifetime);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
} // End Database_Query
|
@@ -0,0 +1,211 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query builder. See [Query Builder](/database/query/builder) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
abstract class Kohana_Database_Query_Builder extends Database_Query {
|
||||
|
||||
/**
|
||||
* Compiles an array of JOIN statements into an SQL partial.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @param array join statements
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_join(Database $db, array $joins)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
foreach ($joins as $join)
|
||||
{
|
||||
// Compile each of the join statements
|
||||
$statements[] = $join->compile($db);
|
||||
}
|
||||
|
||||
return implode(' ', $statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an array of conditions into an SQL partial. Used for WHERE
|
||||
* and HAVING.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @param array condition statements
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_conditions(Database $db, array $conditions)
|
||||
{
|
||||
$last_condition = NULL;
|
||||
|
||||
$sql = '';
|
||||
foreach ($conditions as $group)
|
||||
{
|
||||
// Process groups of conditions
|
||||
foreach ($group as $logic => $condition)
|
||||
{
|
||||
if ($condition === '(')
|
||||
{
|
||||
if ( ! empty($sql) AND $last_condition !== '(')
|
||||
{
|
||||
// Include logic operator
|
||||
$sql .= ' '.$logic.' ';
|
||||
}
|
||||
|
||||
$sql .= '(';
|
||||
}
|
||||
elseif ($condition === ')')
|
||||
{
|
||||
$sql .= ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! empty($sql) AND $last_condition !== '(')
|
||||
{
|
||||
// Add the logic operator
|
||||
$sql .= ' '.$logic.' ';
|
||||
}
|
||||
|
||||
// Split the condition
|
||||
list($column, $op, $value) = $condition;
|
||||
|
||||
if ($value === NULL)
|
||||
{
|
||||
if ($op === '=')
|
||||
{
|
||||
// Convert "val = NULL" to "val IS NULL"
|
||||
$op = 'IS';
|
||||
}
|
||||
elseif ($op === '!=')
|
||||
{
|
||||
// Convert "val != NULL" to "valu IS NOT NULL"
|
||||
$op = 'IS NOT';
|
||||
}
|
||||
}
|
||||
|
||||
// Database operators are always uppercase
|
||||
$op = strtoupper($op);
|
||||
|
||||
if ($op === 'BETWEEN' AND is_array($value))
|
||||
{
|
||||
// BETWEEN always has exactly two arguments
|
||||
list($min, $max) = $value;
|
||||
|
||||
if (is_string($min) AND array_key_exists($min, $this->_parameters))
|
||||
{
|
||||
// Set the parameter as the minimum
|
||||
$min = $this->_parameters[$min];
|
||||
}
|
||||
|
||||
if (is_string($max) AND array_key_exists($max, $this->_parameters))
|
||||
{
|
||||
// Set the parameter as the maximum
|
||||
$max = $this->_parameters[$max];
|
||||
}
|
||||
|
||||
// Quote the min and max value
|
||||
$value = $db->quote($min).' AND '.$db->quote($max);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_string($value) AND array_key_exists($value, $this->_parameters))
|
||||
{
|
||||
// Set the parameter as the value
|
||||
$value = $this->_parameters[$value];
|
||||
}
|
||||
|
||||
// Quote the entire value normally
|
||||
$value = $db->quote($value);
|
||||
}
|
||||
|
||||
if ($column)
|
||||
{
|
||||
// Apply proper quoting to the column
|
||||
$column = $db->quote_identifier($column);
|
||||
}
|
||||
|
||||
// Append the statement to the query
|
||||
$sql .= trim($column.' '.$op.' '.$value);
|
||||
}
|
||||
|
||||
$last_condition = $condition;
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an array of set values into an SQL partial. Used for UPDATE.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @param array updated values
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_set(Database $db, array $values)
|
||||
{
|
||||
$set = array();
|
||||
foreach ($values as $group)
|
||||
{
|
||||
// Split the set
|
||||
list ($column, $value) = $group;
|
||||
|
||||
// Quote the column name
|
||||
$column = $db->quote_identifier($column);
|
||||
|
||||
if (is_string($value) AND array_key_exists($value, $this->_parameters))
|
||||
{
|
||||
// Use the parameter value
|
||||
$value = $this->_parameters[$value];
|
||||
}
|
||||
|
||||
$set[$column] = $column.' = '.$db->quote($value);
|
||||
}
|
||||
|
||||
return implode(', ', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an array of ORDER BY statements into an SQL partial.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @param array sorting columns
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_order_by(Database $db, array $columns)
|
||||
{
|
||||
$sort = array();
|
||||
foreach ($columns as $group)
|
||||
{
|
||||
list ($column, $direction) = $group;
|
||||
|
||||
if ($direction)
|
||||
{
|
||||
// Make the direction uppercase
|
||||
$direction = strtoupper($direction);
|
||||
}
|
||||
|
||||
if ($column)
|
||||
{
|
||||
// Quote the column, if it has a value
|
||||
$column = $db->quote_identifier($column);
|
||||
}
|
||||
|
||||
$sort[] = trim($column.' '.$direction);
|
||||
}
|
||||
|
||||
return 'ORDER BY '.implode(', ', $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the current builder status.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
abstract public function reset();
|
||||
|
||||
} // End Database_Query_Builder
|
@@ -0,0 +1,89 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query builder for DELETE statements. See [Query Builder](/database/query/builder) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Query_Builder_Delete extends Database_Query_Builder_Where {
|
||||
|
||||
// DELETE FROM ...
|
||||
protected $_table;
|
||||
|
||||
/**
|
||||
* Set the table for a delete.
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($table = NULL)
|
||||
{
|
||||
if ($table)
|
||||
{
|
||||
// Set the inital table name
|
||||
$this->_table = $table;
|
||||
}
|
||||
|
||||
// Start the query with no SQL
|
||||
return parent::__construct(Database::DELETE, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the table to delete from.
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @return $this
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
$this->_table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @return string
|
||||
*/
|
||||
public function compile(Database $db)
|
||||
{
|
||||
// Start a deletion query
|
||||
$query = 'DELETE FROM '.$db->quote_table($this->_table);
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add deletion conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
if ($this->_limit !== NULL)
|
||||
{
|
||||
// Add limiting
|
||||
$query .= ' LIMIT '.$this->_limit;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->_table = NULL;
|
||||
$this->_where = array();
|
||||
|
||||
$this->_parameters = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
} // End Database_Query_Builder_Delete
|
@@ -0,0 +1,171 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query builder for INSERT statements. See [Query Builder](/database/query/builder) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
|
||||
|
||||
// INSERT INTO ...
|
||||
protected $_table;
|
||||
|
||||
// (...)
|
||||
protected $_columns = array();
|
||||
|
||||
// VALUES (...)
|
||||
protected $_values = array();
|
||||
|
||||
/**
|
||||
* Set the table and columns for an insert.
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @param array column names
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($table = NULL, array $columns = NULL)
|
||||
{
|
||||
if ($table)
|
||||
{
|
||||
// Set the inital table name
|
||||
$this->_table = $table;
|
||||
}
|
||||
|
||||
if ($columns)
|
||||
{
|
||||
// Set the column names
|
||||
$this->_columns = $columns;
|
||||
}
|
||||
|
||||
// Start the query with no SQL
|
||||
return parent::__construct(Database::INSERT, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the table to insert into.
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @return $this
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
$this->_table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the columns that will be inserted.
|
||||
*
|
||||
* @param array column names
|
||||
* @return $this
|
||||
*/
|
||||
public function columns(array $columns)
|
||||
{
|
||||
$this->_columns = $columns;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or overwrites values. Multiple value sets can be added.
|
||||
*
|
||||
* @param array values list
|
||||
* @param ...
|
||||
* @return $this
|
||||
*/
|
||||
public function values(array $values)
|
||||
{
|
||||
if ( ! is_array($this->_values))
|
||||
{
|
||||
throw new Kohana_Exception('INSERT INTO ... SELECT statements cannot be combined with INSERT INTO ... VALUES');
|
||||
}
|
||||
|
||||
// Get all of the passed values
|
||||
$values = func_get_args();
|
||||
|
||||
$this->_values = array_merge($this->_values, $values);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a sub-query to for the inserted values.
|
||||
*
|
||||
* @param object Database_Query of SELECT type
|
||||
* @return $this
|
||||
*/
|
||||
public function select(Database_Query $query)
|
||||
{
|
||||
if ($query->type() !== Database::SELECT)
|
||||
{
|
||||
throw new Kohana_Exception('Only SELECT queries can be combined with INSERT queries');
|
||||
}
|
||||
|
||||
$this->_values = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @return string
|
||||
*/
|
||||
public function compile(Database $db)
|
||||
{
|
||||
// Start an insertion query
|
||||
$query = 'INSERT INTO '.$db->quote_table($this->_table);
|
||||
|
||||
// Add the column names
|
||||
$query .= ' ('.implode(', ', array_map(array($db, 'quote_identifier'), $this->_columns)).') ';
|
||||
|
||||
if (is_array($this->_values))
|
||||
{
|
||||
// Callback for quoting values
|
||||
$quote = array($db, 'quote');
|
||||
|
||||
$groups = array();
|
||||
foreach ($this->_values as $group)
|
||||
{
|
||||
foreach ($group as $i => $value)
|
||||
{
|
||||
if (is_string($value) AND isset($this->_parameters[$value]))
|
||||
{
|
||||
// Use the parameter value
|
||||
$group[$i] = $this->_parameters[$value];
|
||||
}
|
||||
}
|
||||
|
||||
$groups[] = '('.implode(', ', array_map($quote, $group)).')';
|
||||
}
|
||||
|
||||
// Add the values
|
||||
$query .= 'VALUES '.implode(', ', $groups);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the sub-query
|
||||
$query .= (string) $this->_values;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->_table = NULL;
|
||||
|
||||
$this->_columns =
|
||||
$this->_values = array();
|
||||
|
||||
$this->_parameters = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
} // End Database_Query_Builder_Insert
|
@@ -0,0 +1,107 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query builder for JOIN statements. See [Query Builder](/database/query/builder) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Query_Builder_Join extends Database_Query_Builder {
|
||||
|
||||
// Type of JOIN
|
||||
protected $_type;
|
||||
|
||||
// JOIN ...
|
||||
protected $_table;
|
||||
|
||||
// ON ...
|
||||
protected $_on = array();
|
||||
|
||||
/**
|
||||
* Creates a new JOIN statement for a table. Optionally, the type of JOIN
|
||||
* can be specified as the second parameter.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string type of JOIN: INNER, RIGHT, LEFT, etc
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($table, $type = NULL)
|
||||
{
|
||||
// Set the table to JOIN on
|
||||
$this->_table = $table;
|
||||
|
||||
if ($type !== NULL)
|
||||
{
|
||||
// Set the JOIN type
|
||||
$this->_type = (string) $type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition for joining.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @return $this
|
||||
*/
|
||||
public function on($c1, $op, $c2)
|
||||
{
|
||||
$this->_on[] = array($c1, $op, $c2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL partial for a JOIN statement and return it.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @return string
|
||||
*/
|
||||
public function compile(Database $db)
|
||||
{
|
||||
if ($this->_type)
|
||||
{
|
||||
$sql = strtoupper($this->_type).' JOIN';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = 'JOIN';
|
||||
}
|
||||
|
||||
// Quote the table name that is being joined
|
||||
$sql .= ' '.$db->quote_table($this->_table).' ON ';
|
||||
|
||||
$conditions = array();
|
||||
foreach ($this->_on as $condition)
|
||||
{
|
||||
// Split the condition
|
||||
list($c1, $op, $c2) = $condition;
|
||||
|
||||
if ($op)
|
||||
{
|
||||
// Make the operator uppercase and spaced
|
||||
$op = ' '.strtoupper($op);
|
||||
}
|
||||
|
||||
// Quote each of the identifiers used for the condition
|
||||
$conditions[] = $db->quote_identifier($c1).$op.' '.$db->quote_identifier($c2);
|
||||
}
|
||||
|
||||
// Concat the conditions "... AND ..."
|
||||
$sql .= '('.implode(' AND ', $conditions).')';
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->_type =
|
||||
$this->_table = NULL;
|
||||
|
||||
$this->_on = array();
|
||||
}
|
||||
|
||||
} // End Database_Query_Builder_Join
|
@@ -0,0 +1,388 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query builder for SELECT statements. See [Query Builder](/database/query/builder) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where {
|
||||
|
||||
// SELECT ...
|
||||
protected $_select = array();
|
||||
|
||||
// DISTINCT
|
||||
protected $_distinct = FALSE;
|
||||
|
||||
// FROM ...
|
||||
protected $_from = array();
|
||||
|
||||
// JOIN ...
|
||||
protected $_join = array();
|
||||
|
||||
// GROUP BY ...
|
||||
protected $_group_by = array();
|
||||
|
||||
// HAVING ...
|
||||
protected $_having = array();
|
||||
|
||||
// OFFSET ...
|
||||
protected $_offset = NULL;
|
||||
|
||||
// The last JOIN statement created
|
||||
protected $_last_join;
|
||||
|
||||
/**
|
||||
* Sets the initial columns to select from.
|
||||
*
|
||||
* @param array column list
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $columns = NULL)
|
||||
{
|
||||
if ( ! empty($columns))
|
||||
{
|
||||
// Set the initial columns
|
||||
$this->_select = $columns;
|
||||
}
|
||||
|
||||
// Start the query with no actual SQL statement
|
||||
parent::__construct(Database::SELECT, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables selecting only unique columns using "SELECT DISTINCT"
|
||||
*
|
||||
* @param boolean enable or disable distinct columns
|
||||
* @return $this
|
||||
*/
|
||||
public function distinct($value)
|
||||
{
|
||||
$this->_distinct = (bool) $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the columns to select from.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param ...
|
||||
* @return $this
|
||||
*/
|
||||
public function select($columns = NULL)
|
||||
{
|
||||
$columns = func_get_args();
|
||||
|
||||
$this->_select = array_merge($this->_select, $columns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the columns to select from, using an array.
|
||||
*
|
||||
* @param array list of column names or aliases
|
||||
* @return $this
|
||||
*/
|
||||
public function select_array(array $columns)
|
||||
{
|
||||
$this->_select = array_merge($this->_select, $columns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the tables to select "FROM ..."
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @param ...
|
||||
* @return $this
|
||||
*/
|
||||
public function from($tables)
|
||||
{
|
||||
$tables = func_get_args();
|
||||
|
||||
$this->_from = array_merge($this->_from, $tables);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds addition tables to "JOIN ...".
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string join type (LEFT, RIGHT, INNER, etc)
|
||||
* @return $this
|
||||
*/
|
||||
public function join($table, $type = NULL)
|
||||
{
|
||||
$this->_join[] = $this->_last_join = new Database_Query_Builder_Join($table, $type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds "ON ..." conditions for the last created JOIN statement.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @return $this
|
||||
*/
|
||||
public function on($c1, $op, $c2)
|
||||
{
|
||||
$this->_last_join->on($c1, $op, $c2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "GROUP BY ..." filter.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param ...
|
||||
* @return $this
|
||||
*/
|
||||
public function group_by($columns)
|
||||
{
|
||||
$columns = func_get_args();
|
||||
|
||||
$this->_group_by = array_merge($this->_group_by, $columns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of and_having()
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column value
|
||||
* @return $this
|
||||
*/
|
||||
public function having($column, $op, $value = NULL)
|
||||
{
|
||||
return $this->and_having($column, $op, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "AND HAVING" condition for the query.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column value
|
||||
* @return $this
|
||||
*/
|
||||
public function and_having($column, $op, $value = NULL)
|
||||
{
|
||||
$this->_having[] = array('AND' => array($column, $op, $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "OR HAVING" condition for the query.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column value
|
||||
* @return $this
|
||||
*/
|
||||
public function or_having($column, $op, $value = NULL)
|
||||
{
|
||||
$this->_having[] = array('OR' => array($column, $op, $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of and_having_open()
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function having_open()
|
||||
{
|
||||
return $this->and_having_open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "AND HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_having_open()
|
||||
{
|
||||
$this->_having[] = array('AND' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "OR HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_having_open()
|
||||
{
|
||||
$this->_having[] = array('OR' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function having_close()
|
||||
{
|
||||
return $this->and_having_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_having_close()
|
||||
{
|
||||
$this->_having[] = array('AND' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "OR HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_having_close()
|
||||
{
|
||||
$this->_having[] = array('OR' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start returning results after "OFFSET ..."
|
||||
*
|
||||
* @param integer starting result number
|
||||
* @return $this
|
||||
*/
|
||||
public function offset($number)
|
||||
{
|
||||
$this->_offset = (int) $number;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @return string
|
||||
*/
|
||||
public function compile(Database $db)
|
||||
{
|
||||
// Callback to quote identifiers
|
||||
$quote_ident = array($db, 'quote_identifier');
|
||||
|
||||
// Callback to quote tables
|
||||
$quote_table = array($db, 'quote_table');
|
||||
|
||||
// Start a selection query
|
||||
$query = 'SELECT ';
|
||||
|
||||
if ($this->_distinct === TRUE)
|
||||
{
|
||||
// Select only unique results
|
||||
$query .= 'DISTINCT ';
|
||||
}
|
||||
|
||||
if (empty($this->_select))
|
||||
{
|
||||
// Select all columns
|
||||
$query .= '*';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select all columns
|
||||
$query .= implode(', ', array_unique(array_map($quote_ident, $this->_select)));
|
||||
}
|
||||
|
||||
if ( ! empty($this->_from))
|
||||
{
|
||||
// Set tables to select from
|
||||
$query .= ' FROM '.implode(', ', array_unique(array_map($quote_table, $this->_from)));
|
||||
}
|
||||
|
||||
if ( ! empty($this->_join))
|
||||
{
|
||||
// Add tables to join
|
||||
$query .= ' '.$this->_compile_join($db, $this->_join);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add selection conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_group_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' GROUP BY '.implode(', ', array_map($quote_ident, $this->_group_by));
|
||||
}
|
||||
|
||||
if ( ! empty($this->_having))
|
||||
{
|
||||
// Add filtering conditions
|
||||
$query .= ' HAVING '.$this->_compile_conditions($db, $this->_having);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
if ($this->_limit !== NULL)
|
||||
{
|
||||
// Add limiting
|
||||
$query .= ' LIMIT '.$this->_limit;
|
||||
}
|
||||
|
||||
if ($this->_offset !== NULL)
|
||||
{
|
||||
// Add offsets
|
||||
$query .= ' OFFSET '.$this->_offset;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->_select =
|
||||
$this->_from =
|
||||
$this->_join =
|
||||
$this->_where =
|
||||
$this->_group_by =
|
||||
$this->_having =
|
||||
$this->_order_by = array();
|
||||
|
||||
$this->_distinct = FALSE;
|
||||
|
||||
$this->_limit =
|
||||
$this->_offset =
|
||||
$this->_last_join = NULL;
|
||||
|
||||
$this->_parameters = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
} // End Database_Query_Select
|
@@ -0,0 +1,130 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query builder for UPDATE statements. See [Query Builder](/database/query/builder) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Query_Builder_Update extends Database_Query_Builder_Where {
|
||||
|
||||
// UPDATE ...
|
||||
protected $_table;
|
||||
|
||||
// SET ...
|
||||
protected $_set = array();
|
||||
|
||||
/**
|
||||
* Set the table for a update.
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($table = NULL)
|
||||
{
|
||||
if ($table)
|
||||
{
|
||||
// Set the inital table name
|
||||
$this->_table = $table;
|
||||
}
|
||||
|
||||
// Start the query with no SQL
|
||||
return parent::__construct(Database::UPDATE, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the table to update.
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @return $this
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
$this->_table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the values to update with an associative array.
|
||||
*
|
||||
* @param array associative (column => value) list
|
||||
* @return $this
|
||||
*/
|
||||
public function set(array $pairs)
|
||||
{
|
||||
foreach ($pairs as $column => $value)
|
||||
{
|
||||
$this->_set[] = array($column, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a single column.
|
||||
*
|
||||
* @param mixed table name or array($table, $alias) or object
|
||||
* @param mixed column value
|
||||
* @return $this
|
||||
*/
|
||||
public function value($column, $value)
|
||||
{
|
||||
$this->_set[] = array($column, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param object Database instance
|
||||
* @return string
|
||||
*/
|
||||
public function compile(Database $db)
|
||||
{
|
||||
// Start an update query
|
||||
$query = 'UPDATE '.$db->quote_table($this->_table);
|
||||
|
||||
// Add the columns to update
|
||||
$query .= ' SET '.$this->_compile_set($db, $this->_set);
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add selection conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
if ($this->_limit !== NULL)
|
||||
{
|
||||
// Add limiting
|
||||
$query .= ' LIMIT '.$this->_limit;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->_table = NULL;
|
||||
|
||||
$this->_set =
|
||||
$this->_where = array();
|
||||
|
||||
$this->_limit = NULL;
|
||||
|
||||
$this->_parameters = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
} // End Database_Query_Builder_Update
|
@@ -0,0 +1,160 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database query builder for WHERE statements. See [Query Builder](/database/query/builder) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
abstract class Kohana_Database_Query_Builder_Where extends Database_Query_Builder {
|
||||
|
||||
// WHERE ...
|
||||
protected $_where = array();
|
||||
|
||||
// ORDER BY ...
|
||||
protected $_order_by = array();
|
||||
|
||||
// LIMIT ...
|
||||
protected $_limit = NULL;
|
||||
|
||||
/**
|
||||
* Alias of and_where()
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column value
|
||||
* @return $this
|
||||
*/
|
||||
public function where($column, $op, $value)
|
||||
{
|
||||
return $this->and_where($column, $op, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "AND WHERE" condition for the query.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column value
|
||||
* @return $this
|
||||
*/
|
||||
public function and_where($column, $op, $value)
|
||||
{
|
||||
$this->_where[] = array('AND' => array($column, $op, $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "OR WHERE" condition for the query.
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string logic operator
|
||||
* @param mixed column value
|
||||
* @return $this
|
||||
*/
|
||||
public function or_where($column, $op, $value)
|
||||
{
|
||||
$this->_where[] = array('OR' => array($column, $op, $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of and_where_open()
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where_open()
|
||||
{
|
||||
return $this->and_where_open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "AND WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_where_open()
|
||||
{
|
||||
$this->_where[] = array('AND' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "OR WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_where_open()
|
||||
{
|
||||
$this->_where[] = array('OR' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where_close()
|
||||
{
|
||||
return $this->and_where_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_where_close()
|
||||
{
|
||||
$this->_where[] = array('AND' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "OR WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_where_close()
|
||||
{
|
||||
$this->_where[] = array('OR' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies sorting with "ORDER BY ..."
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param string direction of sorting
|
||||
* @return $this
|
||||
*/
|
||||
public function order_by($column, $direction = NULL)
|
||||
{
|
||||
$this->_order_by[] = array($column, $direction);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return up to "LIMIT ..." results
|
||||
*
|
||||
* @param integer maximum results to return
|
||||
* @return $this
|
||||
*/
|
||||
public function limit($number)
|
||||
{
|
||||
$this->_limit = (int) $number;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
} // End Database_Query_Builder_Where
|
@@ -0,0 +1,331 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database result wrapper. See [Results](/database/results) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query/Result
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
abstract class Kohana_Database_Result implements Countable, Iterator, SeekableIterator, ArrayAccess {
|
||||
|
||||
// Executed SQL for this result
|
||||
protected $_query;
|
||||
|
||||
// Raw result resource
|
||||
protected $_result;
|
||||
|
||||
// Total number of rows and current row
|
||||
protected $_total_rows = 0;
|
||||
protected $_current_row = 0;
|
||||
|
||||
// Return rows as an object or associative array
|
||||
protected $_as_object;
|
||||
|
||||
// Parameters for __construct when using object results
|
||||
protected $_object_params = NULL;
|
||||
|
||||
/**
|
||||
* Sets the total number of rows and stores the result locally.
|
||||
*
|
||||
* @param mixed query result
|
||||
* @param string SQL query
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($result, $sql, $as_object = FALSE, array $params = NULL)
|
||||
{
|
||||
// Store the result locally
|
||||
$this->_result = $result;
|
||||
|
||||
// Store the SQL locally
|
||||
$this->_query = $sql;
|
||||
|
||||
if (is_object($as_object))
|
||||
{
|
||||
// Get the object class name
|
||||
$as_object = get_class($as_object);
|
||||
}
|
||||
|
||||
// Results as objects or associative arrays
|
||||
$this->_as_object = $as_object;
|
||||
|
||||
if ($params)
|
||||
{
|
||||
// Object constructor params
|
||||
$this->_object_params = $params;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result destruction cleans up all open result sets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function __destruct();
|
||||
|
||||
/**
|
||||
* Get a cached database result from the current result iterator.
|
||||
*
|
||||
* $cachable = serialize($result->cached());
|
||||
*
|
||||
* @return Database_Result_Cached
|
||||
* @since 3.0.5
|
||||
*/
|
||||
public function cached()
|
||||
{
|
||||
return new Database_Result_Cached($this->as_array(), $this->_query, $this->_as_object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the rows in the result as an array.
|
||||
*
|
||||
* // Indexed array of all rows
|
||||
* $rows = $result->as_array();
|
||||
*
|
||||
* // Associative array of rows by "id"
|
||||
* $rows = $result->as_array('id');
|
||||
*
|
||||
* // Associative array of rows, "id" => "name"
|
||||
* $rows = $result->as_array('id', 'name');
|
||||
*
|
||||
* @param string column for associative keys
|
||||
* @param string column for values
|
||||
* @return array
|
||||
*/
|
||||
public function as_array($key = NULL, $value = NULL)
|
||||
{
|
||||
$results = array();
|
||||
|
||||
if ($key === NULL AND $value === NULL)
|
||||
{
|
||||
// Indexed rows
|
||||
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[] = $row;
|
||||
}
|
||||
}
|
||||
elseif ($key === NULL)
|
||||
{
|
||||
// Indexed columns
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[] = $row->$value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[] = $row[$value];
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($value === NULL)
|
||||
{
|
||||
// Associative rows
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row->$key] = $row;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row[$key]] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Associative columns
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row->$key] = $row->$value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row[$key]] = $row[$value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->rewind();
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named column from the current row.
|
||||
*
|
||||
* // Get the "id" value
|
||||
* $id = $result->get('id');
|
||||
*
|
||||
* @param string column to get
|
||||
* @param mixed default value if the column does not exist
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = NULL)
|
||||
{
|
||||
$row = $this->current();
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
if (isset($row->$name))
|
||||
return $row->$name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($row[$name]))
|
||||
return $row[$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Countable::count], returns the total number of rows.
|
||||
*
|
||||
* echo count($result);
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->_total_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetExists], determines if row exists.
|
||||
*
|
||||
* if (isset($result[10]))
|
||||
* {
|
||||
* // Row 10 exists
|
||||
* }
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return ($offset >= 0 AND $offset < $this->_total_rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetGet], gets a given row.
|
||||
*
|
||||
* $row = $result[10];
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if ( ! $this->seek($offset))
|
||||
return NULL;
|
||||
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetSet], throws an error.
|
||||
*
|
||||
* [!!] You cannot modify a database result.
|
||||
*
|
||||
* @return void
|
||||
* @throws Kohana_Exception
|
||||
*/
|
||||
final public function offsetSet($offset, $value)
|
||||
{
|
||||
throw new Kohana_Exception('Database results are read-only');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetUnset], throws an error.
|
||||
*
|
||||
* [!!] You cannot modify a database result.
|
||||
*
|
||||
* @return void
|
||||
* @throws Kohana_Exception
|
||||
*/
|
||||
final public function offsetUnset($offset)
|
||||
{
|
||||
throw new Kohana_Exception('Database results are read-only');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::key], returns the current row number.
|
||||
*
|
||||
* echo key($result);
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->_current_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::next], moves to the next row.
|
||||
*
|
||||
* next($result);
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
++$this->_current_row;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::prev], moves to the previous row.
|
||||
*
|
||||
* prev($result);
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
--$this->_current_row;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::rewind], sets the current row to zero.
|
||||
*
|
||||
* rewind($result);
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->_current_row = 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::valid], checks if the current row exists.
|
||||
*
|
||||
* [!!] This method is only used internally.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->offsetExists($this->_current_row);
|
||||
}
|
||||
|
||||
} // End Database_Result
|
@@ -0,0 +1,51 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Object used for caching the results of select queries. See [Results](/database/results#select-cached) for usage and examples.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Query/Result
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Database_Result_Cached extends Database_Result {
|
||||
|
||||
public function __construct(array $result, $sql, $as_object = NULL)
|
||||
{
|
||||
parent::__construct($result, $sql, $as_object);
|
||||
|
||||
// Find the number of rows in the result
|
||||
$this->_total_rows = count($result);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Cached results do not use resources
|
||||
}
|
||||
|
||||
public function cached()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function seek($offset)
|
||||
{
|
||||
if ($this->offsetExists($offset))
|
||||
{
|
||||
$this->_current_row = $offset;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
// Return an array of the row
|
||||
return $this->valid() ? $this->_result[$this->_current_row] : FALSE;
|
||||
}
|
||||
|
||||
} // End Database_Result_Cached
|
139
includes/kohana/modules/database/classes/kohana/db.php
Normal file
139
includes/kohana/modules/database/classes/kohana/db.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Provides a shortcut to get Database related objects for [making queries](../database/query).
|
||||
*
|
||||
* Shortcut | Returned Object
|
||||
* -------------|---------------
|
||||
* [`DB::query()`](#query) | [Database_Query]
|
||||
* [`DB::insert()`](#insert) | [Database_Query_Builder_Insert]
|
||||
* [`DB::select()`](#select),<br />[`DB::select_array()`](#select_array) | [Database_Query_Builder_Select]
|
||||
* [`DB::update()`](#update) | [Database_Query_Builder_Update]
|
||||
* [`DB::delete()`](#delete) | [Database_Query_Builder_Delete]
|
||||
* [`DB::expr()`](#expr) | [Database_Expression]
|
||||
*
|
||||
* You pass the same parameters to these functions as you pass to the objects they return.
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_DB {
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query] of the given type.
|
||||
*
|
||||
* // Create a new SELECT query
|
||||
* $query = DB::query(Database::SELECT, 'SELECT * FROM users');
|
||||
*
|
||||
* // Create a new DELETE query
|
||||
* $query = DB::query(Database::DELETE, 'DELETE FROM users WHERE id = 5');
|
||||
*
|
||||
* Specifying the type changes the returned result. When using
|
||||
* `Database::SELECT`, a [Database_Query_Result] will be returned.
|
||||
* `Database::INSERT` queries will return the insert id and number of rows.
|
||||
* For all other queries, the number of affected rows is returned.
|
||||
*
|
||||
* @param integer type: Database::SELECT, Database::UPDATE, etc
|
||||
* @param string SQL statement
|
||||
* @return Database_Query
|
||||
*/
|
||||
public static function query($type, $sql)
|
||||
{
|
||||
return new Database_Query($type, $sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Select]. Each argument will be
|
||||
* treated as a column. To generate a `foo AS bar` alias, use an array.
|
||||
*
|
||||
* // SELECT id, username
|
||||
* $query = DB::select('id', 'username');
|
||||
*
|
||||
* // SELECT id AS user_id
|
||||
* $query = DB::select(array('id', 'user_id'));
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param ...
|
||||
* @return Database_Query_Builder_Select
|
||||
*/
|
||||
public static function select($columns = NULL)
|
||||
{
|
||||
return new Database_Query_Builder_Select(func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Select] from an array of columns.
|
||||
*
|
||||
* // SELECT id, username
|
||||
* $query = DB::select_array(array('id', 'username'));
|
||||
*
|
||||
* @param array columns to select
|
||||
* @return Database_Query_Builder_Select
|
||||
*/
|
||||
public static function select_array(array $columns = NULL)
|
||||
{
|
||||
return new Database_Query_Builder_Select($columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Insert].
|
||||
*
|
||||
* // INSERT INTO users (id, username)
|
||||
* $query = DB::insert('users', array('id', 'username'));
|
||||
*
|
||||
* @param string table to insert into
|
||||
* @param array list of column names or array($column, $alias) or object
|
||||
* @return Database_Query_Builder_Insert
|
||||
*/
|
||||
public static function insert($table = NULL, array $columns = NULL)
|
||||
{
|
||||
return new Database_Query_Builder_Insert($table, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Update].
|
||||
*
|
||||
* // UPDATE users
|
||||
* $query = DB::update('users');
|
||||
*
|
||||
* @param string table to update
|
||||
* @return Database_Query_Builder_Update
|
||||
*/
|
||||
public static function update($table = NULL)
|
||||
{
|
||||
return new Database_Query_Builder_Update($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Delete].
|
||||
*
|
||||
* // DELETE FROM users
|
||||
* $query = DB::delete('users');
|
||||
*
|
||||
* @param string table to delete from
|
||||
* @return Database_Query_Builder_Delete
|
||||
*/
|
||||
public static function delete($table = NULL)
|
||||
{
|
||||
return new Database_Query_Builder_Delete($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Expression] which is not escaped. An expression
|
||||
* is the only way to use SQL functions within query builders.
|
||||
*
|
||||
* $expression = DB::expr('COUNT(users.id)');
|
||||
* $query = DB::update('users')->set(array('login_count' => DB::expr('login_count + 1')))->where('id', '=', $id);
|
||||
* $users = ORM::factory('user')->where(DB::expr("BINARY `hash`"), '=', $hash)->find();
|
||||
*
|
||||
* @param string expression
|
||||
* @return Database_Expression
|
||||
*/
|
||||
public static function expr($string)
|
||||
{
|
||||
return new Database_Expression($string);
|
||||
}
|
||||
|
||||
} // End DB
|
@@ -0,0 +1,229 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* Database-based session class.
|
||||
*
|
||||
* Sample schema:
|
||||
*
|
||||
* CREATE TABLE `sessions` (
|
||||
* `session_id` VARCHAR( 24 ) NOT NULL,
|
||||
* `last_active` INT UNSIGNED NOT NULL,
|
||||
* `contents` TEXT NOT NULL,
|
||||
* PRIMARY KEY ( `session_id` ),
|
||||
* INDEX ( `last_active` )
|
||||
* ) ENGINE = MYISAM ;
|
||||
*
|
||||
* @package Kohana/Database
|
||||
* @category Session
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class Kohana_Session_Database extends Session {
|
||||
|
||||
// Database instance
|
||||
protected $_db;
|
||||
|
||||
// Database table name
|
||||
protected $_table = 'sessions';
|
||||
|
||||
// Database column names
|
||||
protected $_columns = array(
|
||||
'session_id' => 'session_id',
|
||||
'last_active' => 'last_active',
|
||||
'contents' => 'contents'
|
||||
);
|
||||
|
||||
// Garbage collection requests
|
||||
protected $_gc = 500;
|
||||
|
||||
// The current session id
|
||||
protected $_session_id;
|
||||
|
||||
// The old session id
|
||||
protected $_update_id;
|
||||
|
||||
public function __construct(array $config = NULL, $id = NULL)
|
||||
{
|
||||
if ( ! isset($config['group']))
|
||||
{
|
||||
// Use the default group
|
||||
$config['group'] = 'default';
|
||||
}
|
||||
|
||||
// Load the database
|
||||
$this->_db = Database::instance($config['group']);
|
||||
|
||||
if (isset($config['table']))
|
||||
{
|
||||
// Set the table name
|
||||
$this->_table = (string) $config['table'];
|
||||
}
|
||||
|
||||
if (isset($config['gc']))
|
||||
{
|
||||
// Set the gc chance
|
||||
$this->_gc = (int) $config['gc'];
|
||||
}
|
||||
|
||||
if (isset($config['columns']))
|
||||
{
|
||||
// Overload column names
|
||||
$this->_columns = $config['columns'];
|
||||
}
|
||||
|
||||
parent::__construct($config, $id);
|
||||
|
||||
if (mt_rand(0, $this->_gc) === $this->_gc)
|
||||
{
|
||||
// Run garbage collection
|
||||
// This will average out to run once every X requests
|
||||
$this->_gc();
|
||||
}
|
||||
}
|
||||
|
||||
public function id()
|
||||
{
|
||||
return $this->_session_id;
|
||||
}
|
||||
|
||||
protected function _read($id = NULL)
|
||||
{
|
||||
if ($id OR $id = Cookie::get($this->_name))
|
||||
{
|
||||
$result = DB::select(array($this->_columns['contents'], 'contents'))
|
||||
->from($this->_table)
|
||||
->where($this->_columns['session_id'], '=', ':id')
|
||||
->limit(1)
|
||||
->param(':id', $id)
|
||||
->execute($this->_db);
|
||||
|
||||
if ($result->count())
|
||||
{
|
||||
// Set the current session id
|
||||
$this->_session_id = $this->_update_id = $id;
|
||||
|
||||
// Return the contents
|
||||
return $result->get('contents');
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new session id
|
||||
$this->_regenerate();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
protected function _regenerate()
|
||||
{
|
||||
// Create the query to find an ID
|
||||
$query = DB::select($this->_columns['session_id'])
|
||||
->from($this->_table)
|
||||
->where($this->_columns['session_id'], '=', ':id')
|
||||
->limit(1)
|
||||
->bind(':id', $id);
|
||||
|
||||
do
|
||||
{
|
||||
// Create a new session id
|
||||
$id = str_replace('.', '-', uniqid(NULL, TRUE));
|
||||
|
||||
// Get the the id from the database
|
||||
$result = $query->execute($this->_db);
|
||||
}
|
||||
while ($result->count());
|
||||
|
||||
return $this->_session_id = $id;
|
||||
}
|
||||
|
||||
protected function _write()
|
||||
{
|
||||
if ($this->_update_id === NULL)
|
||||
{
|
||||
// Insert a new row
|
||||
$query = DB::insert($this->_table, $this->_columns)
|
||||
->values(array(':new_id', ':active', ':contents'));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the row
|
||||
$query = DB::update($this->_table)
|
||||
->value($this->_columns['last_active'], ':active')
|
||||
->value($this->_columns['contents'], ':contents')
|
||||
->where($this->_columns['session_id'], '=', ':old_id');
|
||||
|
||||
if ($this->_update_id !== $this->_session_id)
|
||||
{
|
||||
// Also update the session id
|
||||
$query->value($this->_columns['session_id'], ':new_id');
|
||||
}
|
||||
}
|
||||
|
||||
$query
|
||||
->param(':new_id', $this->_session_id)
|
||||
->param(':old_id', $this->_update_id)
|
||||
->param(':active', $this->_data['last_active'])
|
||||
->param(':contents', $this->__toString());
|
||||
|
||||
// Execute the query
|
||||
$query->execute($this->_db);
|
||||
|
||||
// The update and the session id are now the same
|
||||
$this->_update_id = $this->_session_id;
|
||||
|
||||
// Update the cookie with the new session id
|
||||
Cookie::set($this->_name, $this->_session_id, $this->_lifetime);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
protected function _destroy()
|
||||
{
|
||||
if ($this->_update_id === NULL)
|
||||
{
|
||||
// Session has not been created yet
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Delete the current session
|
||||
$query = DB::delete($this->_table)
|
||||
->where($this->_columns['session_id'], '=', ':id')
|
||||
->param(':id', $this->_update_id);
|
||||
|
||||
try
|
||||
{
|
||||
// Execute the query
|
||||
$query->execute($this->_db);
|
||||
|
||||
// Delete the cookie
|
||||
Cookie::delete($this->_name);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// An error occurred, the session has not been deleted
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
protected function _gc()
|
||||
{
|
||||
if ($this->_lifetime)
|
||||
{
|
||||
// Expire sessions when their lifetime is up
|
||||
$expires = $this->_lifetime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Expire sessions after one month
|
||||
$expires = Date::MONTH;
|
||||
}
|
||||
|
||||
// Delete all sessions that have expired
|
||||
DB::delete($this->_table)
|
||||
->where($this->_columns['last_active'], '<', ':time')
|
||||
->param(':time', time() - $expires)
|
||||
->execute($this->_db);
|
||||
}
|
||||
|
||||
} // End Session_Database
|
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class Session_Database extends Kohana_Session_Database {}
|
57
includes/kohana/modules/database/config/database.php
Normal file
57
includes/kohana/modules/database/config/database.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
return array
|
||||
(
|
||||
'default' => array
|
||||
(
|
||||
'type' => 'mysql',
|
||||
'connection' => array(
|
||||
/**
|
||||
* The following options are available for MySQL:
|
||||
*
|
||||
* string hostname server hostname, or socket
|
||||
* string database database name
|
||||
* string username database username
|
||||
* string password database password
|
||||
* boolean persistent use persistent connections?
|
||||
*
|
||||
* Ports and sockets may be appended to the hostname.
|
||||
*/
|
||||
'hostname' => 'localhost',
|
||||
'database' => 'kohana',
|
||||
'username' => FALSE,
|
||||
'password' => FALSE,
|
||||
'persistent' => FALSE,
|
||||
),
|
||||
'table_prefix' => '',
|
||||
'charset' => 'utf8',
|
||||
'caching' => FALSE,
|
||||
'profiling' => TRUE,
|
||||
),
|
||||
'alternate' => array(
|
||||
'type' => 'pdo',
|
||||
'connection' => array(
|
||||
/**
|
||||
* The following options are available for PDO:
|
||||
*
|
||||
* string dsn Data Source Name
|
||||
* string username database username
|
||||
* string password database password
|
||||
* boolean persistent use persistent connections?
|
||||
*/
|
||||
'dsn' => 'mysql:host=localhost;dbname=kohana',
|
||||
'username' => 'root',
|
||||
'password' => 'r00tdb',
|
||||
'persistent' => FALSE,
|
||||
),
|
||||
/**
|
||||
* The following extra options are available for PDO:
|
||||
*
|
||||
* string identifier set the escaping identifier
|
||||
*/
|
||||
'table_prefix' => '',
|
||||
'charset' => 'utf8',
|
||||
'caching' => FALSE,
|
||||
'profiling' => TRUE,
|
||||
),
|
||||
);
|
27
includes/kohana/modules/database/config/session.php
Normal file
27
includes/kohana/modules/database/config/session.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
return array(
|
||||
'database' => array(
|
||||
/**
|
||||
* Database settings for session storage.
|
||||
*
|
||||
* string group configuation group name
|
||||
* string table session table name
|
||||
* integer gc number of requests before gc is invoked
|
||||
* columns array custom column names
|
||||
*/
|
||||
'group' => 'default',
|
||||
'table' => 'sessions',
|
||||
'gc' => 500,
|
||||
'columns' => array(
|
||||
/**
|
||||
* session_id: session identifier
|
||||
* last_active: timestamp of the last activity
|
||||
* contents: serialized session data
|
||||
*/
|
||||
'session_id' => 'session_id',
|
||||
'last_active' => 'last_active',
|
||||
'contents' => 'contents'
|
||||
),
|
||||
),
|
||||
);
|
23
includes/kohana/modules/database/config/userguide.php
Normal file
23
includes/kohana/modules/database/config/userguide.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
return array(
|
||||
// Leave this alone
|
||||
'modules' => array(
|
||||
|
||||
// This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename'
|
||||
'database' => array(
|
||||
|
||||
// Whether this modules userguide pages should be shown
|
||||
'enabled' => TRUE,
|
||||
|
||||
// The name that should show up on the userguide index page
|
||||
'name' => 'Database',
|
||||
|
||||
// A short description of this module, shown on the index page
|
||||
'description' => 'Database agnostic querying and result management.',
|
||||
|
||||
// Copyright message, shown in the footer for this module
|
||||
'copyright' => '© 2008–2010 Kohana Team',
|
||||
)
|
||||
)
|
||||
);
|
118
includes/kohana/modules/database/guide/database/config.md
Normal file
118
includes/kohana/modules/database/guide/database/config.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Configuration
|
||||
|
||||
The default config file is located in `MODPATH/database/config/database.php`. You should copy this file to `APPPATH/config/database.php` and make changes there, in keeping with the [cascading filesystem](../kohana/files).
|
||||
|
||||
The database configuration file contains an array of configuration groups. The structure of each database configuration group, called an "instance", looks like this:
|
||||
|
||||
string INSTANCE_NAME => array(
|
||||
'type' => string DATABASE_TYPE,
|
||||
'connection' => array CONNECTION_ARRAY,
|
||||
'table_prefix' => string TABLE_PREFIX,
|
||||
'charset' => string CHARACTER_SET,
|
||||
'profiling' => boolean QUERY_PROFILING,
|
||||
),
|
||||
|
||||
Understanding each of these settings is important.
|
||||
|
||||
INSTANCE_NAME
|
||||
: Connections can be named anything you want, but you should always have at least one connection called "default".
|
||||
|
||||
DATABASE_TYPE
|
||||
: One of the installed database drivers. Kohana comes with "mysql" and "pdo" drivers. Drivers must extend the Database class.
|
||||
|
||||
CONNECTION_ARRAY
|
||||
: Specific driver options for connecting to your database. (Driver options are explained [below](#connection-settings).)
|
||||
|
||||
TABLE_PREFIX
|
||||
: Prefix that will be added to all table names by the [query builder](#query_building). Prepared statements will **not** use the table prefix.
|
||||
|
||||
QUERY_PROFILING
|
||||
: Enables [profiling](../kohana/profiling) of database queries. This is useful for seeing how many queries each page is using, and which are taking the longest. You must enable the profiler the view these stats.
|
||||
|
||||
## Example
|
||||
|
||||
The example file below shows 2 MySQL connections, one local and one remote.
|
||||
|
||||
return array
|
||||
(
|
||||
'default' => array
|
||||
(
|
||||
'type' => 'mysql',
|
||||
'connection' => array(
|
||||
'hostname' => 'localhost',
|
||||
'username' => 'dbuser',
|
||||
'password' => 'mypassword',
|
||||
'persistent' => FALSE,
|
||||
'database' => 'my_db_name',
|
||||
),
|
||||
'table_prefix' => '',
|
||||
'charset' => 'utf8',
|
||||
'profiling' => TRUE,
|
||||
),
|
||||
'remote' => array(
|
||||
'type' => 'mysql',
|
||||
'connection' => array(
|
||||
'hostname' => '55.55.55.55',
|
||||
'username' => 'remote_user',
|
||||
'password' => 'mypassword',
|
||||
'persistent' => FALSE,
|
||||
'database' => 'my_remote_db_name',
|
||||
),
|
||||
'table_prefix' => '',
|
||||
'charset' => 'utf8',
|
||||
'profiling' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
## Connections and Instances
|
||||
|
||||
Each configuration group is referred to as a database instance. Each instance can be accessed by calling [Database::instance]. If you don't provide a parameter, the default instance is used.
|
||||
|
||||
// This would connect to the database defined as 'default'
|
||||
$default = Database::instance();
|
||||
|
||||
// This would connect to the database defined as 'remote'
|
||||
$remote = Database::instance('remote');
|
||||
|
||||
To disconnect the database, simply destroy the object:
|
||||
|
||||
unset($default)
|
||||
|
||||
// Or
|
||||
|
||||
unset(Database::$instances['default']);
|
||||
|
||||
If you want to disconnect all of the database instances at once:
|
||||
|
||||
Database::$instances = array();
|
||||
|
||||
## Connection Settings
|
||||
|
||||
Every database driver has different connection settings.
|
||||
|
||||
### MySQL
|
||||
|
||||
A [MySQL database](http://www.php.net/manual/en/book.mysql.php) can accept the following options in the `connection` array:
|
||||
|
||||
Type | Option | Description | Default value
|
||||
----------|------------|----------------------------| -------------------------
|
||||
`string` | hostname | Hostname of the database | `localhost`
|
||||
`integer` | port | Port number | `NULL`
|
||||
`string` | socket | UNIX socket | `NULL`
|
||||
`string` | username | Database username | `NULL`
|
||||
`string` | password | Database password | `NULL`
|
||||
`boolean` | persistent | Persistent connections | `FALSE`
|
||||
`string` | database | Database name | `kohana`
|
||||
|
||||
### PDO
|
||||
|
||||
A [PDO database](http://php.net/manual/en/book.pdo.php) can accept these options in the `connection` array:
|
||||
|
||||
Type | Option | Description | Default value
|
||||
----------|------------|----------------------------| -------------------------
|
||||
`string` | dsn | PDO data source identifier | `localhost`
|
||||
`string` | username | Database username | `NULL`
|
||||
`string` | password | Database password | `NULL`
|
||||
`boolean` | persistent | Persistent connections | `FALSE`
|
||||
|
||||
[!!] If you are using PDO and are not sure what to use for the `dsn` option, review [PDO::__construct](http://php.net/pdo.construct).
|
52
includes/kohana/modules/database/guide/database/examples.md
Normal file
52
includes/kohana/modules/database/guide/database/examples.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Examples
|
||||
|
||||
Here are some "real world" examples of using the database library to construct your queries and use the results.
|
||||
|
||||
## Examples of Prepared Statements
|
||||
|
||||
TODO: 4-6 examples of prepared statements of varying complexity, including a good bind() example.
|
||||
|
||||
## Pagination and search/filter
|
||||
|
||||
In this example, we loop through an array of whitelisted input fields and for each allowed non-empty value we add it to the search query. We make a clone of the query and then execute that query to count the total number of results. The count is then passed to the [Pagination](../pagination) class to determine the search offset. The last few lines search with Pagination's items_per_page and offset values to return a page of results based on the current page the user is on.
|
||||
|
||||
$query = DB::select()->from('users');
|
||||
|
||||
//only search for these fields
|
||||
$form_inputs = array('first_name', 'last_name', 'email');
|
||||
foreach ($form_inputs as $name)
|
||||
{
|
||||
$value = Arr::get($_GET, $name, FALSE);
|
||||
if ($value !== FALSE AND $value != '')
|
||||
{
|
||||
$query->where($name, 'like', '%'.$value.'%');
|
||||
}
|
||||
}
|
||||
|
||||
//copy the query & execute it
|
||||
$pagination_query = clone $query;
|
||||
$count = $pagination_query->select('COUNT("*") AS mycount')->execute()->get('mycount');
|
||||
|
||||
//pass the total item count to Pagination
|
||||
$config = Kohana::config('pagination');
|
||||
$pagination = Pagination::factory(array(
|
||||
'total_items' => $count,
|
||||
'current_page' => array('source' => 'route', 'key' => 'page'),
|
||||
'items_per_page' => 20,
|
||||
'view' => 'pagination/pretty',
|
||||
'auto_hide' => TRUE,
|
||||
));
|
||||
$page_links = $pagination->render();
|
||||
|
||||
//search for results starting at the offset calculated by the Pagination class
|
||||
$query->order_by('last_name', 'asc')
|
||||
->order_by('first_name', 'asc')
|
||||
->limit($pagination->items_per_page)
|
||||
->offset($pagination->offset);
|
||||
$results = $query->execute()->as_array();
|
||||
|
||||
## Having
|
||||
|
||||
TODO: example goes here
|
||||
|
||||
[!!] We could use more examples on this page.
|
17
includes/kohana/modules/database/guide/database/index.md
Normal file
17
includes/kohana/modules/database/guide/database/index.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Database
|
||||
|
||||
Kohana 3.0 comes with a robust module for working with databases. By default, the database module supports drivers for [MySQL](http://php.net/mysql) and [PDO](http://php.net/pdo), but new drivers can be made for other database servers.
|
||||
|
||||
The database module is included with the Kohana 3.0 install, but needs to be enabled before you can use it. To enable, open your `application/bootstrap.php` file and modify the call to [Kohana::modules] by including the database module like so:
|
||||
|
||||
Kohana::modules(array(
|
||||
...
|
||||
'database' => MODPATH.'database',
|
||||
...
|
||||
));
|
||||
|
||||
Next, you will then need to [configure](config) the database module to connect to your database.
|
||||
|
||||
Once that is done then you can make [queries](query) and use the [results](results).
|
||||
|
||||
The database module also provides a [config driver](../api/Kohana_Config_Database) (for storing [configuration](../kohana/files/config) in the database) and a [session driver](Session_Database).
|
7
includes/kohana/modules/database/guide/database/menu.md
Normal file
7
includes/kohana/modules/database/guide/database/menu.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## [Database]()
|
||||
- [Configuration](config)
|
||||
- [Querying](query)
|
||||
- [Prepared Statements](query/prepared)
|
||||
- [Query Builder](query/builder)
|
||||
- [Results](results)
|
||||
- [Examples](examples)
|
5
includes/kohana/modules/database/guide/database/query.md
Normal file
5
includes/kohana/modules/database/guide/database/query.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Making Queries
|
||||
|
||||
There are two different ways to make queries. The simplest way to make a query is to use [Database_Query], via [DB::query], to manually create queries. These queries are called [prepared statements](query/prepared) and allow you to set query parameters which are automatically escaped. The second way to make a query is by building the query using method calls. This is done using the [query builder](query/builder).
|
||||
|
||||
[!!] All queries are run using the `execute` method, which accepts a [Database] object or instance name. See [Database_Query::execute] for more information.
|
253
includes/kohana/modules/database/guide/database/query/builder.md
Normal file
253
includes/kohana/modules/database/guide/database/query/builder.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# Query Builder
|
||||
|
||||
Creating queries dynamically using objects and methods allows queries to be written very quickly in an agnostic way. Query building also adds identifier (table and column name) quoting, as well as value quoting.
|
||||
|
||||
[!!] At this time, it is not possible to combine query building with prepared statements.
|
||||
|
||||
## Select
|
||||
|
||||
Each type of database query is represented by a different class, each with their own methods. For instance, to create a SELECT query, we use [DB::select] which is a shortcut to return a new [Database_Query_Builder_Select] object:
|
||||
|
||||
$query = DB::select();
|
||||
|
||||
Query Builder methods return a reference to itself so that method chaining may be used. Select queries ussually require a table and they are referenced using the `from()` method. The `from()` method takes one parameter which can be the table name (string), an array of two strings (table name and alias), or an object (See Subqueries in the Advanced Queries section below).
|
||||
|
||||
$query = DB::select()->from('users');
|
||||
|
||||
Limiting the results of queries is done using the `where()`, `and_where()` and `or_where()` methods. These methods take three parameters: a column, an operator, and a value.
|
||||
|
||||
$query = DB::select()->from('users')->where('username', '=', 'john');
|
||||
|
||||
Multiple `where()` methods may be used to string together multiple clauses connected by the boolean operator in the method's prefix. The `where()` method is a wrapper that just calls `and_where()`.
|
||||
|
||||
$query = DB::select()->from('users')->where('username', '=', 'john')->or_where('username', '=', 'jane');
|
||||
|
||||
You can use any operator you want. Examples include `IN`, `BETWEEN`, `>`, `=<`, `!=`, etc. Use an array for operators that require more than one value.
|
||||
|
||||
$query = DB::select()->from('users')->where('logins', '<=', 1);
|
||||
|
||||
$query = DB::select()->from('users')->where('logins', '>', 50);
|
||||
|
||||
$query = DB::select()->from('users')->where('username', 'IN', array('john','mark','matt'));
|
||||
|
||||
$query = DB::select()->from('users')->where('joindate', 'BETWEEN', array($then, $now));
|
||||
|
||||
By default, [DB::select] will select all columns (`SELECT * ...`), but you can also specify which columns you want returned by passing parameters to [DB::select]:
|
||||
|
||||
$query = DB::select('username', 'password')->from('users')->where('username', '=', 'john');
|
||||
|
||||
Now take a minute to look at what this method chain is doing. First, we create a new selection object using the [DB::select] method. Next, we set table(s) using the `from()` method. Last, we search for a specific records using the `where()` method. We can display the SQL that will be executed by casting the query to a string:
|
||||
|
||||
echo Kohana::debug((string) $query);
|
||||
// Should display:
|
||||
// SELECT `username`, `password` FROM `users` WHERE `username` = 'john'
|
||||
|
||||
Notice how the column and table names are automatically escaped, as well as the values? This is one of the key benefits of using the query builder.
|
||||
|
||||
### Select - AS (column aliases)
|
||||
|
||||
It is also possible to create `AS` aliases when selecting, by passing an array as each parameter to [DB::select]:
|
||||
|
||||
$query = DB::select(array('username', 'u'), array('password', 'p'))->from('users');
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
SELECT `username` AS `u`, `password` AS `p` FROM `users`
|
||||
|
||||
### Select - DISTINCT
|
||||
|
||||
Unique column values may be turned on or off (default) by passing TRUE or FALSE, respectively, to the `distinct()` method.
|
||||
|
||||
$query = DB::select('username')->distinct(TRUE)->from('posts');
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
SELECT DISTINCT `username` FROM `posts`
|
||||
|
||||
### Select - LIMIT & OFFSET
|
||||
|
||||
When querying large sets of data, it is often better to limit the results and page through the data one chunk at a time. This is done using the `limit()` and `offset()` methods.
|
||||
|
||||
$query = DB::select()->from(`posts`)->limit(10)->offset(30);
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
SELECT * FROM `posts` LIMIT 10 OFFSET 30
|
||||
|
||||
### Select - ORDER BY
|
||||
|
||||
Often you will want the results in a particular order and rather than sorting the results, it's better to have the results returned to you in the correct order. You can do this by using the order_by() method. It takes the column name and an optional direction string as the parameters. Multiple `order_by()` methods can be used to add additional sorting capability.
|
||||
|
||||
$query = DB::select()->from(`posts`)->order_by(`published`, `DESC`);
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
SELECT * FROM `posts` ORDER BY `published` DESC
|
||||
|
||||
[!!] For a complete list of methods available while building a select query see [Database_Query_Builder_Select].
|
||||
|
||||
## Insert
|
||||
|
||||
To create records into the database, use [DB::insert] to create an INSERT query, using `values()` to pass in the data:
|
||||
|
||||
$query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd'));
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd')
|
||||
|
||||
[!!] For a complete list of methods available while building an insert query see [Database_Query_Builder_Insert].
|
||||
|
||||
## Update
|
||||
|
||||
To modify an existing record, use [DB::update] to create an UPDATE query:
|
||||
|
||||
$query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john');
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john'
|
||||
|
||||
[!!] For a complete list of methods available while building an update query see [Database_Query_Builder_Update].
|
||||
|
||||
## Delete
|
||||
|
||||
To remove an existing record, use [DB::delete] to create a DELETE query:
|
||||
|
||||
$query = DB::delete('users')->where('username', 'IN', array('john', 'jane'));
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
DELETE FROM `users` WHERE `username` IN ('john', 'jane')
|
||||
|
||||
[!!] For a complete list of methods available while building a delete query see [Database_Query_Builder_Delete].
|
||||
|
||||
## Advanced Queries
|
||||
|
||||
### Joins
|
||||
|
||||
Multiple tables can be joined using the `join()` and `on()` methods. The `join()` method takes two parameters. The first is either a table name, an array containing the table and alias, or an object (subquery or expression). The second parameter is the join type: LEFT, RIGHT, INNER, etc.
|
||||
|
||||
The `on()` method sets the conditions for the previous `join()` method and is very similar to the `where()` method in that it takes three parameters; left column (name or object), an operator, and the right column (name or object). Multiple `on()` methods may be used to supply multiple conditions and they will be appended with an 'AND' operator.
|
||||
|
||||
// This query will find all the posts related to "smith" with JOIN
|
||||
$query = DB::select('authors.name', 'posts.content')->from('authors')->join('posts')->on('authors.id', '=', 'posts.author_id')->where('authors.name', '=', 'smith');
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
SELECT `authors`.`name`, `posts`.`content` FROM `authors` JOIN `posts` ON (`authors`.`id` = `posts`.`author_id`) WHERE `authors`.`name` = 'smith'
|
||||
|
||||
If you want to do a LEFT, RIGHT or INNER JOIN you would do it like this `join('colum_name', 'type_of_join')`:
|
||||
|
||||
// This query will find all the posts related to "smith" with LEFT JOIN
|
||||
$query = DB::select()->from('authors')->join('posts', 'LEFT')->on('authors.id', '=', 'posts.author_id')->where('authors.name', '=', 'smith');
|
||||
|
||||
This query would generate the following SQL:
|
||||
|
||||
SELECT `authors`.`name`, `posts`.`content` FROM `authors` LEFT JOIN `posts` ON (`authors`.`id` = `posts`.`author_id`) WHERE `authors`.`name` = 'smith'
|
||||
|
||||
[!!] When joining multiple tables with similar column names, it's best to prefix the columns with the table name or table alias to avoid errors. Ambiguous column names should also be aliased so that they can be referenced easier.
|
||||
|
||||
### Database Functions
|
||||
|
||||
Eventually you will probably run into a situation where you need to call `COUNT` or some other database function within your query. The query builder supports these functions in two ways. The first is by using quotes within aliases:
|
||||
|
||||
$query = DB::select(array('COUNT("username")', 'total_users'))->from('users');
|
||||
|
||||
This looks almost exactly the same as a standard `AS` alias, but note how the column name is wrapped in double quotes. Any time a double-quoted value appears inside of a column name, **only** the part inside the double quotes will be escaped. This query would generate the following SQL:
|
||||
|
||||
SELECT COUNT(`username`) AS `total_users` FROM `users`
|
||||
|
||||
[!!] When building complex queries and you need to get a count of the total rows that will be returned, build the expression with an empty column list first. Then clone the query and add the COUNT function to one copy and the columns list to the other. This will cut down on the total lines of code and make updating the query easier.
|
||||
|
||||
$query = DB::select()->from('users')
|
||||
->join('posts')->on('posts.username', '=', 'users.username')
|
||||
->where('users.active', '=', TRUE)
|
||||
->where('posts.created', '>=', $yesterday);
|
||||
|
||||
$total = clone $query;
|
||||
$total->select(array('COUNT( DISTINCT "username")', 'unique_users'));
|
||||
$query->select('posts.username')->distinct();
|
||||
|
||||
### Aggregate Functions
|
||||
|
||||
Aggregate functions like `COUNT()`, `SUM()`, `AVG()`, etc. will most likely be used with the `group_by()` and possibly the `having()` methods in order to group and filter the results on a set of columns.
|
||||
|
||||
$query = DB::select('username', array('COUNT("id")', 'total_posts')
|
||||
->from('posts')->group_by('username')->having('total_posts', '>=', 10);
|
||||
|
||||
This will generate the following query:
|
||||
|
||||
SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10
|
||||
|
||||
### Subqueries
|
||||
|
||||
Query Builder objects can be passed as parameters to many of the methods to create subqueries. Let's take the previous example query and pass it to a new query.
|
||||
|
||||
$sub = DB::select('username', array('COUNT("id")', 'total_posts')
|
||||
->from('posts')->group_by('username')->having('total_posts', '>=', 10);
|
||||
|
||||
$query = DB::select('profiles.*', 'posts.total_posts')->from('profiles')
|
||||
->join(array($sub, 'posts'), 'INNER')->on('profiles.username', '=', 'posts.username');
|
||||
|
||||
This will generate the following query:
|
||||
|
||||
SELECT `profiles`.*, `posts`.`total_posts` FROM `profiles` INNER JOIN
|
||||
( SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10 ) AS posts
|
||||
ON `profiles`.`username` = `posts`.`username`
|
||||
|
||||
Insert queries can also use a select query for the input values
|
||||
|
||||
$sub = DB::select('username', array('COUNT("id")', 'total_posts')
|
||||
->from('posts')->group_by('username')->having('total_posts', '>=', 10);
|
||||
|
||||
$query = DB::insert('post_totals', array('username', 'posts'))->select($sub);
|
||||
|
||||
This will generate the following query:
|
||||
|
||||
INSERT INTO `post_totals` (`username`, `posts`)
|
||||
SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10
|
||||
|
||||
### Boolean Operators and Nested Clauses
|
||||
|
||||
Multiple Where and Having clauses are added to the query with Boolean operators connecting each expression. The default operator for both methods is AND which is the same as the and_ prefixed method. The OR operator can be specified by prefixing the methods with or_. Where and Having clauses can be nested or grouped by post fixing either method with _open and then followed by a method with a _close.
|
||||
|
||||
$query = DB::select()->from('users')
|
||||
->where_open()
|
||||
->or_where('id', 'IN', $expired)
|
||||
->and_where_open()
|
||||
->where('last_login', '<=', $last_month)
|
||||
->or_where('last_login', 'IS', NULL)
|
||||
->and_where_close()
|
||||
->where_close()
|
||||
->and_where('removed','IS', NULL);
|
||||
|
||||
This will generate the following query:
|
||||
|
||||
SELECT * FROM `users` WHERE ( `id` IN (1, 2, 3, 5) OR ( `last_login` <= 1276020805 OR `last_login` IS NULL ) ) AND `removed` IS NULL
|
||||
|
||||
### Database Expressions
|
||||
|
||||
There are cases were you need a complex expression or other database functions, which you don't want the Query Builder to try and escape. In these cases, you will need to use a database expression created with [DB::expr]. **A database expression is taken as direct input and no escaping is performed.**
|
||||
|
||||
$query = DB::update('users')->set(array('login_count' => DB::expr('login_count + 1')))->where('id', '=', $id);
|
||||
|
||||
This will generate the following query, assuming `$id = 45`:
|
||||
|
||||
UPDATE `users` SET `login_count` = `login_count` + 1 WHERE `id` = 45
|
||||
|
||||
Another example to calculate the distance of two geographical points:
|
||||
|
||||
$query = DB::select(array(DB::expr('degrees(acos(sin(radians('.$lat.')) * sin(radians(`latitude`)) + cos(radians('.$lat.')) * cos(radians(`latitude`)) * cos(radians(abs('.$lng.' - `longitude`))))) * 69.172'), 'distance'))->from('locations');
|
||||
|
||||
[!!] You must validate or escape any user input inside of DB::expr as it will obviously not be escaped it for you.
|
||||
|
||||
## Executing
|
||||
|
||||
Once you are done building, you can execute the query using `execute()` and use [the results](results).
|
||||
|
||||
$result = $query->execute();
|
||||
|
||||
To use a different database [config group](config) pass either the name or the config object to `execute()`.
|
||||
|
||||
$result = $query->execute('config_name')
|
@@ -0,0 +1,67 @@
|
||||
# Prepared Statements
|
||||
|
||||
Using prepared statements allows you to write SQL queries manually while still escaping the query values automatically to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Creating a query is simple:
|
||||
|
||||
$query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user');
|
||||
|
||||
The [DB::query] method is just a shortcut that creates a new [Database_Query] class for us, to allow method chaining. The query contains a `:user` parameter, which we will get to in a second.
|
||||
|
||||
The first parameter of [DB::query] is the type of query. It should be `Database::SELECT`, `Database::INSERT`, `Database::UPDATE`, or `Database::DELETE`. This is done for compatibility reasons for drivers, and to easily determine what `execute()` should return.
|
||||
|
||||
The second parameter is the query itself. Rather than trying to concatenate your query and variables together, you should make use of [Database_Query::param]. This will make your queries much easier to mantain, and will escape the values to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection).
|
||||
|
||||
## Parameters
|
||||
|
||||
Our example query earlier contains a `:user` parameter, which we can assign to a value using [Database_Query::param] like so:
|
||||
|
||||
$query->param(':user', 'john');
|
||||
|
||||
[!!] Parameter names can be any unique string, as they are replaced using [strtr](http://php.net/strtr). It is highly recommended to **not** use dollars signs as parameter names to prevent confusion. Colons are commonly used.
|
||||
|
||||
You can also update the `:user` parameter by calling [Database_Query::param] again:
|
||||
|
||||
$query->param(':user', $_GET['search']);
|
||||
|
||||
If you want to set multiple parameters at once, you can use [Database_Query::parameters].
|
||||
|
||||
$query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user AND status = :status');
|
||||
|
||||
$query->parameters(array(
|
||||
':user' => 'john',
|
||||
':status' => 'active',
|
||||
));
|
||||
|
||||
It is also possible to bind a parameter to a variable, using a [variable reference]((http://php.net/language.references.whatdo)). This can be extremely useful when running the same query many times:
|
||||
|
||||
$query = DB::query(Database::INSERT, 'INSERT INTO users (username, password) VALUES (:user, :pass)')
|
||||
->bind(':user', $username)
|
||||
->bind(':pass', $password);
|
||||
|
||||
foreach ($new_users as $username => $password)
|
||||
{
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
In the above example, the variables `$username` and `$password` are changed for every loop of the `foreach` statement. When the parameter changes, it effectively changes the `:user` and `:pass` query parameters. Careful parameter binding can save a lot of code when it is used properly.
|
||||
|
||||
The only difference between `param()` and `bind()` is that `bind()` passes the variable by reference rather than by assignment (copied), so future changes to the variable can be "seen" by the query.
|
||||
|
||||
[!!] Although all parameters are escaped to prevent SQL injection, it is still a good idea to validate/sanitize your input.
|
||||
|
||||
## Display the raw query
|
||||
|
||||
If you want to display the SQL that will be executed, simply cast the object to a string:
|
||||
|
||||
echo Kohana::debug((string) $query);
|
||||
// Should display:
|
||||
// SELECT * FROM users WHERE username = 'john'
|
||||
|
||||
## Executing
|
||||
|
||||
Once you have assigned something to each of the parameters, you can execute the query using `execute()` and use [the results](results).
|
||||
|
||||
$result = $query->execute();
|
||||
|
||||
To use a different database [config group](config) pass either the name or the config object to `execute()`.
|
||||
|
||||
$result = $query->execute('config_name')
|
105
includes/kohana/modules/database/guide/database/results.md
Normal file
105
includes/kohana/modules/database/guide/database/results.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Results
|
||||
|
||||
## Execute
|
||||
|
||||
Once you have a query object built, either through a prepared statement or through the builder, you must then `execute()` the query and retrieve the results. Depending on the query type used, the results returned will vary.
|
||||
|
||||
## Select
|
||||
|
||||
[DB::select] will return a [Database_Result] object which you can then iterate over. This example shows how you can iterate through the [Database_Result] using a foreach.
|
||||
|
||||
$results = DB::select()->from('users')->where('verified', '=', 0)->execute();
|
||||
foreach($results as $user)
|
||||
{
|
||||
// Send reminder email to $user['email']
|
||||
echo $user['email']." needs to verify his/her account\n";
|
||||
}
|
||||
|
||||
### Select - `as_object()` and `as_assoc()`
|
||||
|
||||
When iterating over a result set, the default type will be an associative array with the column names or aliases as the keys. As an option, before calling `execute()`, you can specify to return the result rows as an object by using the `as_object()` method. The `as_object()` method takes one parameter, the name of the class of your choice, but will default to TRUE which uses the `stdClass`. Here is the example again using `stdClass`.
|
||||
|
||||
$results = DB::select()->from('users')->where('verified', '=', 0)->as_object()->execute();
|
||||
foreach($results as $user)
|
||||
{
|
||||
// Send reminder email to $user->email
|
||||
echo $user->email." needs to verify his/her account\n";
|
||||
}
|
||||
|
||||
[!!] The method `as_assoc()` will remove the object name and return the results set back to an associative array. Since this is the default, this method is seldom required.
|
||||
|
||||
### Select - `as_array()`
|
||||
|
||||
Sometimes you will require the results as a pure array rather than as an object. The `Database_Result` method `as_array()` will return an array of all rows.
|
||||
|
||||
$results = DB::select('id', 'email')->from('users')->execute();
|
||||
$users = $results->as_array();
|
||||
foreach($users as $user)
|
||||
{
|
||||
echo 'User ID: '.$user['id'];
|
||||
echo 'User Email: '.$user['email'];
|
||||
}
|
||||
|
||||
It also accepts two parameters that can be very helpful: `$key` and `$value`. When passing a value to `$key` you will index the resulting array by the column specified.
|
||||
|
||||
$results = DB::select('id', 'email')->from('users')->execute();
|
||||
$users = $results->as_array('id');
|
||||
foreach($users as $id => $user)
|
||||
{
|
||||
echo 'User ID: '.$id;
|
||||
echo 'User Email: '.$user['email'];
|
||||
}
|
||||
|
||||
The second parameter, `$value`, will reference the column specified and return that value rather than the whole row. This is particularly useful when making `<select>` dropdowns.
|
||||
|
||||
$results = DB::select('id', 'name')->from('users')->execute();
|
||||
$users = $results->as_array('id','name');
|
||||
// Show a dropdown with all users in it.
|
||||
echo Form::select('author', $users)
|
||||
|
||||
To return a non-associative array, leave `$key` as NULL and just pass a `$value`.
|
||||
|
||||
$results = DB::select('email')->from('users')->execute();
|
||||
$users = $results->as_array(NULL, 'email');
|
||||
foreach($users as $email)
|
||||
{
|
||||
echo 'User Email: '.$email;
|
||||
}
|
||||
|
||||
### Select - `get()`
|
||||
|
||||
Sometime you only want a single value from a query. The `get()` method returns the value of the named column from the current row. The second parameter, `$default`, is used to supply a default value when the result is NULL.
|
||||
|
||||
$total_users = DB::select(array('COUNT("username")', 'total_users'))->from('users')->execute()->get('total_users', 0);
|
||||
|
||||
### Select - `cached()`
|
||||
|
||||
The mysql database driver returns a `Database_Result` that works with a MySQL Resource data type. Since this resource lives outside of PHP environment, it can't be serialized which means it also can't be cached. To get around this the `Database_Result` object has the `cached()` method that returns a `Database_Result_Cached` object of the result set. The `Database_Result_Cached` can be serialized and cached, but can take up more memory.
|
||||
|
||||
[!!] NOTE: Currently, the PDO diver always returns a class of `Database_Result_Cached`, so `cached()` just returns itself.
|
||||
|
||||
The `cached()` function doesn't actually do any caching, it simply returns the result in a way that can be serialized and cached. You will need to use the [Cache Module](../cache) or some other caching method.
|
||||
|
||||
### Select - `count()`
|
||||
|
||||
The `Database_Result` object implements the `Countable` Interface. The method `count()` returns the total row count in the result set.
|
||||
|
||||
[!!] NOTE: This is the count of the current result set, not a count of how many records are in the database. This is important to point out especially when using `limit()` and `offset()` in your query.
|
||||
|
||||
[!!] For a complete list of methods available when working with a result set see [Database_Result].
|
||||
|
||||
## Insert
|
||||
|
||||
[DB::insert] returns an array of two values: the last insert id and the number of affected rows.
|
||||
|
||||
$insert = DB::insert('tools')
|
||||
->columns(array('name', 'model', 'description'))
|
||||
->values(array('Skil 3400 10" Table Saw', '3400', 'Powerful 15 amp motor; weighs just 54-pounds'));
|
||||
|
||||
list($insert_id, $affected_rows) = $insert->execute();
|
||||
|
||||
## Update & Delete
|
||||
|
||||
[DB::update] and [DB::delete] both return the number of affected rows as an integer.
|
||||
|
||||
$rows_deleted = DB::delete('tools')->where('model', 'like', '3400')->execute();
|
Reference in New Issue
Block a user