1
0
This repository has been archived on 2021-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
gallery3-contrib/3.0/obsolete/web_client/system/libraries/Database_Builder.php

1006 lines
19 KiB
PHP

<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database builder
*
* @package Kohana
* @author Kohana Team
* @copyright (c) 2008-2009 Kohana Team
* @license http://kohanaphp.com/license
*/
class Database_Builder_Core {
// Valid ORDER BY directions
protected $order_directions = array('ASC', 'DESC', 'RAND()');
// Database object
protected $db;
// Builder members
protected $select = array();
protected $from = array();
protected $join = array();
protected $where = array();
protected $group_by = array();
protected $having = array();
protected $order_by = array();
protected $limit = NULL;
protected $offset = NULL;
protected $set = array();
protected $columns = array();
protected $values = array();
protected $type;
protected $distinct = FALSE;
// TTL for caching (using Cache library)
protected $ttl = FALSE;
public function __construct($db = 'default')
{
$this->db = $db;
}
/**
* Resets all query components
*/
public function reset()
{
$this->select = array();
$this->from = array();
$this->join = array();
$this->where = array();
$this->group_by = array();
$this->having = array();
$this->order_by = array();
$this->limit = NULL;
$this->offset = NULL;
$this->set = array();
$this->values = array();
}
public function __toString()
{
return $this->compile();
}
/**
* Compiles the builder object into a SQL query
*
* @return string Compiled query
*/
protected function compile()
{
if ( ! is_object($this->db))
{
// Use default database for compiling to string if none is given
$this->db = Database::instance($this->db);
}
if ($this->type === Database::SELECT)
{
// SELECT columns FROM table
$sql = $this->distinct ? 'SELECT DISTINCT ' : 'SELECT ';
$sql .= $this->compile_select();
if ( ! empty($this->from))
{
$sql .= "\nFROM ".$this->compile_from();
}
}
elseif ($this->type === Database::UPDATE)
{
$sql = 'UPDATE '.$this->compile_from()."\n".'SET '.$this->compile_set();
}
elseif ($this->type === Database::INSERT)
{
$sql = 'INSERT INTO '.$this->compile_from()."\n".$this->compile_columns()."\nVALUES ".$this->compile_values();
}
elseif ($this->type === Database::DELETE)
{
$sql = 'DELETE FROM '.$this->compile_from();
}
if ( ! empty($this->join))
{
$sql .= $this->compile_join();
}
if ( ! empty($this->where))
{
$sql .= "\n".'WHERE '.$this->compile_conditions($this->where);
}
if ( ! empty($this->having))
{
$sql .= "\n".'HAVING '.$this->compile_conditions($this->having);
}
if ( ! empty($this->group_by))
{
$sql .= "\n".'GROUP BY '.$this->compile_group_by();
}
if ( ! empty($this->order_by))
{
$sql .= "\nORDER BY ".$this->compile_order_by();
}
if (is_int($this->limit))
{
$sql .= "\nLIMIT ".$this->limit;
}
if (is_int($this->offset))
{
$sql .= "\nOFFSET ".$this->offset;
}
return $sql;
}
/**
* Compiles the SELECT portion of the query
*
* @return string
*/
protected function compile_select()
{
$vals = array();
foreach ($this->select as $alias => $name)
{
if (is_string($alias))
{
$vals[] = $this->db->quote_column($name, $alias);
}
else
{
$vals[] = $this->db->quote_column($name);
}
}
return implode(', ', $vals);
}
/**
* Compiles the FROM portion of the query
*
* @return string
*/
protected function compile_from()
{
$vals = array();
foreach ($this->from as $alias => $name)
{
if (is_string($alias))
{
// Using AS format so escape both
$vals[] = $this->db->quote_table($name, $alias);
}
else
{
// Just using the table name itself
$vals[] = $this->db->quote_table($name);
}
}
return implode(', ', $vals);
}
/**
* Compiles the JOIN portion of the query
*
* @return string
*/
protected function compile_join()
{
$sql = '';
foreach ($this->join as $join)
{
list($table, $keys, $type) = $join;
if ($type !== NULL)
{
// Join type
$sql .= ' '.$type;
}
$sql .= ' JOIN '.$this->db->quote_table($table);
$condition = '';
if ($keys instanceof Database_Expression)
{
$condition = (string) $keys;
}
elseif (is_array($keys))
{
// ON condition is an array of matches
foreach ($keys as $key => $value)
{
if ( ! empty($condition))
{
$condition .= ' AND ';
}
$condition .= $this->db->quote_column($key).' = '.$this->db->quote_column($value);
}
}
if ( ! empty($condition))
{
// Add ON condition
$sql .= ' ON ('.$condition.')';
}
}
return $sql;
}
/**
* Compiles the GROUP BY portion of the query
*
* @return string
*/
protected function compile_group_by()
{
$vals = array();
foreach ($this->group_by as $column)
{
// Escape the column
$vals[] = $this->db->quote_column($column);
}
return implode(', ', $vals);
}
/**
* Compiles the ORDER BY portion of the query
*
* @return string
*/
public function compile_order_by()
{
$ordering = array();
foreach ($this->order_by as $column => $order_by)
{
list($column, $direction) = each($order_by);
$column = $this->db->quote_column($column);
if ($direction !== NULL)
{
$direction = ' '.$direction;
}
$ordering[] = $column.$direction;
}
return implode(', ', $ordering);
}
/**
* Compiles the SET portion of the query for UPDATE
*
* @return string
*/
public function compile_set()
{
$vals = array();
foreach ($this->set as $key => $value)
{
// Using an UPDATE so Key = Val
$vals[] = $this->db->quote_column($key).' = '.$this->db->quote($value);
}
return implode(', ', $vals);
}
/**
* Join tables to the builder
*
* @param mixed Table name
* @param mixed Key, or an array of key => value pair, for join condition (can be a Database_Expression)
* @param mixed Value if $keys is not an array or Database_Expression
* @param string Join type (LEFT, RIGHT, INNER, etc.)
* @return Database_Builder
*/
public function join($table, $keys, $value = NULL, $type = NULL)
{
if (is_string($keys))
{
$keys = array($keys => $value);
}
if ($type !== NULL)
{
$type = strtoupper($type);
}
$this->join[] = array($table, $keys, $type);
return $this;
}
/**
* Add tables to the FROM portion of the builder
*
* @param string|array table name or array(alias => table)
* @return Database_Builder
*/
public function from($tables)
{
if ( ! is_array($tables))
{
$tables = func_get_args();
}
$this->from = array_merge($this->from, $tables);
return $this;
}
/**
* Add fields to the GROUP BY portion
*
* @param mixed Field names or an array of fields
* @return Database_Builder
*/
public function group_by($columns)
{
if ( ! is_array($columns))
{
$columns = func_get_args();
}
$this->group_by = array_merge($this->group_by, $columns);
return $this;
}
/**
* Add conditions to the HAVING clause (AND)
*
* @param mixed Column name or array of columns => vals
* @param string Operation to perform
* @param mixed Value
* @return Database_Builder
*/
public function having($columns, $op = '=', $value = NULL)
{
return $this->and_having($columns, $op, $value);
}
/**
* Add conditions to the HAVING clause (AND)
*
* @param mixed Column name or array of triplets
* @param string Operation to perform
* @param mixed Value
* @return Database_Builder
*/
public function and_having($columns, $op = '=', $value = NULL)
{
if (is_array($columns))
{
foreach ($columns as $column)
{
$this->having[] = array('AND' => $column);
}
}
else
{
$this->having[] = array('AND' => array($columns, $op, $value));
}
return $this;
}
/**
* Add conditions to the HAVING clause (OR)
*
* @param mixed Column name or array of triplets
* @param string Operation to perform
* @param mixed Value
* @return Database_Builder
*/
public function or_having($columns, $op = '=', $value = NULL)
{
if (is_array($columns))
{
foreach ($columns as $column)
{
$this->having[] = array('OR' => $column);
}
}
else
{
$this->having[] = array('OR' => array($columns, $op, $value));
}
return $this;
}
/**
* Add fields to the ORDER BY portion
*
* @param mixed Field names or an array of fields (field => direction)
* @param string Direction or NULL for ascending
* @return Database_Builder
*/
public function order_by($columns, $direction = NULL)
{
if (is_array($columns))
{
foreach ($columns as $column => $direction)
{
if (is_string($column))
{
$this->order_by[] = array($column => $direction);
}
else
{
// $direction is the column name when the array key is numeric
$this->order_by[] = array($direction => NULL);
}
}
}
else
{
$this->order_by[] = array($columns => $direction);
}
return $this;
}
/**
* Limit rows returned
*
* @param int Number of rows
* @return Database_Builder
*/
public function limit($number)
{
$this->limit = (int) $number;
return $this;
}
/**
* Offset into result set
*
* @param int Offset
* @return Database_Builder
*/
public function offset($number)
{
$this->offset = (int) $number;
return $this;
}
public function left_join($table, $keys, $value = NULL)
{
return $this->join($table, $keys, $value, 'LEFT');
}
public function right_join($table, $keys, $value = NULL)
{
return $this->join($table, $keys, $value, 'RIGHT');
}
public function inner_join($table, $keys, $value = NULL)
{
return $this->join($table, $keys, $value, 'INNER');
}
public function outer_join($table, $keys, $value = NULL)
{
return $this->join($table, $keys, $value, 'OUTER');
}
public function full_join($table, $keys, $value = NULL)
{
return $this->join($table, $keys, $value, 'FULL');
}
public function left_inner_join($table, $keys, $value = NULL)
{
return $this->join($table, $keys, $value, 'LEFT INNER');
}
public function right_inner_join($table, $keys, $value = NULL)
{
return $this->join($table, $keys, $value, 'RIGHT INNER');
}
public function open($clause = 'WHERE')
{
return $this->and_open($clause);
}
public function and_open($clause = 'WHERE')
{
if ($clause === 'WHERE')
{
$this->where[] = array('AND' => '(');
}
else
{
$this->having[] = array('AND' => '(');
}
return $this;
}
public function or_open($clause = 'WHERE')
{
if ($clause === 'WHERE')
{
$this->where[] = array('OR' => '(');
}
else
{
$this->having[] = array('OR' => '(');
}
return $this;
}
public function close($clause = 'WHERE')
{
if ($clause === 'WHERE')
{
$this->where[] = array(')');
}
else
{
$this->having[] = array(')');
}
return $this;
}
/**
* Add conditions to the WHERE clause (AND)
*
* @param mixed Column name or array of columns => vals
* @param string Operation to perform
* @param mixed Value
* @return Database_Builder
*/
public function where($columns, $op = '=', $value = NULL)
{
return $this->and_where($columns, $op, $value);
}
/**
* Add conditions to the WHERE clause (AND)
*
* @param mixed Column name or array of triplets
* @param string Operation to perform
* @param mixed Value
* @return Database_Builder
*/
public function and_where($columns, $op = '=', $value = NULL)
{
if (is_array($columns))
{
foreach ($columns as $column)
{
$this->where[] = array('AND' => $column);
}
}
else
{
$this->where[] = array('AND' => array($columns, $op, $value));
}
return $this;
}
/**
* Add conditions to the WHERE clause (OR)
*
* @param mixed Column name or array of triplets
* @param string Operation to perform
* @param mixed Value
* @return Database_Builder
*/
public function or_where($columns, $op = '=', $value = NULL)
{
if (is_array($columns))
{
foreach ($columns as $column)
{
$this->where[] = array('OR' => $column);
}
}
else
{
$this->where[] = array('OR' => array($columns, $op, $value));
}
return $this;
}
/**
* Compiles the given clause's conditions
*
* @param array Clause conditions
* @return string
*/
protected function compile_conditions($groups)
{
$last_condition = NULL;
$sql = '';
foreach ($groups 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
{
list($columns, $op, $value) = $condition;
// Stores each individual condition
$vals = array();
if ($columns instanceof Database_Expression)
{
// Add directly to condition list
$vals[] = (string) $columns;
}
else
{
$op = strtoupper($op);
if ( ! is_array($columns))
{
$columns = array($columns => $value);
}
foreach ($columns as $column => $value)
{
if ($value instanceof Database_Builder)
{
// Using a subquery
$value->db = $this->db;
$value = '('.(string) $value.')';
}
elseif (is_array($value))
{
if ($op === 'BETWEEN' OR $op === 'NOT BETWEEN')
{
// Falls between two values
$value = $this->db->quote($value[0]).' AND '.$this->db->quote($value[1]);
}
else
{
// Return as list
$value = array_map(array($this->db, 'quote'), $value);
$value = '('.implode(', ', $value).')';
}
}
else
{
$value = $this->db->quote($value);
}
if ( ! empty($column))
{
// Ignore blank columns
$column = $this->db->quote_column($column);
}
// Add to condition list
$vals[] = $column.' '.$op.' '.$value;
}
}
if ( ! empty($sql) AND $last_condition !== '(')
{
// Add the logic operator
$sql .= ' '.$logic.' ';
}
// Join the condition list items together by the given logic operator
$sql .= implode(' '.$logic.' ', $vals);
}
$last_condition = $condition;
}
}
return $sql;
}
/**
* Set values for UPDATE
*
* @param mixed Column name or array of columns => vals
* @param mixed Value (can be a Database_Expression)
* @return Database_Builder
*/
public function set($keys, $value = NULL)
{
if (is_string($keys))
{
$keys = array($keys => $value);
}
$this->set = array_merge($keys, $this->set);
return $this;
}
/**
* Columns used for INSERT queries
*
* @param array Columns
* @return Database_Builder
*/
public function columns($columns)
{
if ( ! is_array($columns))
{
$columns = func_get_args();
}
$this->columns = $columns;
return $this;
}
/**
* Compiles the columns portion of the query for INSERT
*
* @return string
*/
protected function compile_columns()
{
return '('.implode(', ', array_map(array($this->db, 'quote_column'), $this->columns)).')';
}
/**
* Values used for INSERT queries
*
* @param array Values
* @return Database_Builder
*/
public function values($values)
{
if ( ! is_array($values))
{
$values = func_get_args();
}
$this->values[] = $values;
return $this;
}
/**
* Compiles the VALUES portion of the query for INSERT
*
* @return string
*/
protected function compile_values()
{
$values = array();
foreach ($this->values as $group)
{
// Each set of values to be inserted
$values[] = '('.implode(', ', array_map(array($this->db, 'quote'), $group)).')';
}
return implode(', ', $values);
}
/**
* Create a SELECT query and specify selected columns
*
* @param string|array column name or array(alias => column)
* @return Database_Builder
*/
public function select($columns = NULL)
{
$this->type = Database::SELECT;
if ($columns === NULL)
{
$columns = array('*');
}
elseif ( ! is_array($columns))
{
$columns = func_get_args();
}
$this->select = array_merge($this->select, $columns);
return $this;
}
/**
* Create a SELECT query and specify selected columns
*
* @param string|array column name or array(alias => column)
* @return Database_Builder
*/
public function select_distinct($columns = NULL)
{
$this->select($columns);
$this->distinct = TRUE;
return $this;
}
/**
* Create an UPDATE query
*
* @param string Table name
* @param array Array of Keys => Values
* @param array WHERE conditions
* @return Database_Builder
*/
public function update($table = NULL, $set = NULL, $where = NULL)
{
$this->type = Database::UPDATE;
if (is_array($set))
{
$this->set($set);
}
if ($where !== NULL)
{
$this->where($where);
}
if ($table !== NULL)
{
$this->from($table);
}
return $this;
}
/**
* Create an INSERT query. Use 'columns' and 'values' methods for multi-row inserts
*
* @param string Table name
* @param array Array of Keys => Values
* @return Database_Builder
*/
public function insert($table = NULL, $set = NULL)
{
$this->type = Database::INSERT;
if (is_array($set))
{
$this->columns(array_keys($set));
$this->values(array_values($set));
}
if ($table !== NULL)
{
$this->from($table);
}
return $this;
}
/**
* Create a DELETE query
*
* @param string Table name
* @param array WHERE conditions
* @return Database_Builder
*/
public function delete($table, $where = NULL)
{
$this->type = Database::DELETE;
if ($where !== NULL)
{
$this->where($where);
}
if ($table !== NULL)
{
$this->from($table);
}
return $this;
}
/**
* Count records for a given table
*
* @param string Table name
* @param array WHERE conditions
* @return int
*/
public function count_records($table = FALSE, $where = NULL)
{
if (count($this->from) < 1)
{
if ($table === FALSE)
throw new Database_Exception('Database count_records requires a table');
$this->from($table);
}
if ($where !== NULL)
{
$this->where($where);
}
// Grab the count AS records_found
$result = $this->select(array('records_found' => 'COUNT("*")'))->execute();
return $result->get('records_found');
}
/**
* Executes the built query
*
* @param mixed Database name or object
* @return Database_Result
*/
public function execute($db = NULL)
{
if ($db !== NULL)
{
$this->db = $db;
}
if ( ! is_object($this->db))
{
// Get the database instance
$this->db = Database::instance($this->db);
}
$query = $this->compile();
// Reset the query after executing
$this->reset();
if ($this->ttl !== FALSE AND $this->type === Database::SELECT)
{
// Return result from cache (only allowed with SELECT)
return $this->db->query_cache($query, $this->ttl);
}
else
{
// Load the result (no caching)
return $this->db->query($query);
}
}
/**
* Set caching for the query
*
* @param mixed Time-to-live (FALSE to disable, NULL for Cache default, seconds otherwise)
* @return Database_Builder
*/
public function cache($ttl = NULL)
{
$this->ttl = $ttl;
return $this;
}
} // End Database_Builder