Update SimpleAutocomplete, VMXTemplate, DatabaseMysql
parent
a1dc99440b
commit
cfd6ddddd9
|
@ -3,7 +3,8 @@
|
|||
/**
|
||||
* Very stable interface for MySQL - object-oriented at last :)
|
||||
* Select builder is inspired by MediaWiki's one.
|
||||
* (c) Vitaliy Filippov, 2012
|
||||
* Also usable for querying SphinxQL.
|
||||
* (c) Vitaliy Filippov, 2012-2013
|
||||
*/
|
||||
|
||||
if (!defined('MS_HASH'))
|
||||
|
@ -31,6 +32,7 @@ class DatabaseMysql implements Database
|
|||
var $tableNames = array();
|
||||
var $queryLogFile;
|
||||
var $reconnect = true;
|
||||
var $ondestroy = 'commit';
|
||||
|
||||
var $queryCount = 0;
|
||||
var $link;
|
||||
|
@ -46,8 +48,10 @@ class DatabaseMysql implements Database
|
|||
* dbname DB name to use
|
||||
* username Username
|
||||
* password Password
|
||||
* reconnect Whether to reconnect on idle timeout [true]
|
||||
* tableNames Table name mappings (virtual => real)
|
||||
* queryLogFile Path to query log file
|
||||
* reconnect Whether to reconnect on idle timeout [true]
|
||||
* ondestroy commit/rollback/none during destruction [commit]
|
||||
*/
|
||||
function __construct($options)
|
||||
{
|
||||
|
@ -59,7 +63,9 @@ class DatabaseMysql implements Database
|
|||
'username' => '',
|
||||
'password' => '',
|
||||
'reconnect' => true,
|
||||
'tableNames' => array(),
|
||||
'queryLogFile' => '',
|
||||
'ondestroy' => 'commit',
|
||||
);
|
||||
$options += $defOpts;
|
||||
if ($options['socket'])
|
||||
|
@ -72,6 +78,16 @@ class DatabaseMysql implements Database
|
|||
}
|
||||
}
|
||||
|
||||
function __destruct()
|
||||
{
|
||||
$o = $this->ondestroy;
|
||||
if (($o === 'commit' || $o === 'rollback') && $this->transactions)
|
||||
{
|
||||
$this->transactions = array(false);
|
||||
$this->$o();
|
||||
}
|
||||
}
|
||||
|
||||
function connect()
|
||||
{
|
||||
if ($this->socket !== NULL)
|
||||
|
@ -109,6 +125,18 @@ class DatabaseMysql implements Database
|
|||
return "`".str_replace("`", "``", $name)."`";
|
||||
}
|
||||
|
||||
function quoteInto($str, $params)
|
||||
{
|
||||
$i = 0;
|
||||
$r = '';
|
||||
while (($p = strrpos($str, '?')) !== false)
|
||||
{
|
||||
$r = $this->quote($params[$i++]) . substr($str, $p+1) . $r;
|
||||
$str = substr($str, 0, $p);
|
||||
}
|
||||
return $str.$r;
|
||||
}
|
||||
|
||||
function quote($value)
|
||||
{
|
||||
if ($value === NULL)
|
||||
|
@ -136,16 +164,73 @@ class DatabaseMysql implements Database
|
|||
$r = $this->link->query($sql, $fetchMode);
|
||||
if (!$r)
|
||||
{
|
||||
if ($this->link->errno == 2006 && $this->reconnect)
|
||||
if ($this->link->errno == 2006 && $this->reconnect && !$this->transactions)
|
||||
{
|
||||
// "MySQL server has gone away"
|
||||
$this->link = false;
|
||||
$this->connect();
|
||||
$r = $this->link->query($sql, $fetchMode);
|
||||
if (!$r && $this->link->errno == 2006)
|
||||
{
|
||||
$this->link = false;
|
||||
}
|
||||
}
|
||||
if (!$r)
|
||||
{
|
||||
throw new DatabaseException($this->link->error . "\nQuery: $sql", $this->link->errno);
|
||||
}
|
||||
throw new DatabaseException($this->link->error, $this->link->errno);
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
function multi_select(array $queries, $format = 0)
|
||||
{
|
||||
$this->queryCount += count($queries);
|
||||
$log = '';
|
||||
foreach ($queries as &$sql)
|
||||
{
|
||||
if (!is_string($sql))
|
||||
{
|
||||
$sql = $this->select_builder($sql[0], $sql[1], $sql[2], @$sql[3]);
|
||||
}
|
||||
$log .= date("[Y-m-d H:i:s] ").$sql."\n";
|
||||
}
|
||||
unset($sql);
|
||||
if ($this->queryLogFile)
|
||||
{
|
||||
file_put_contents($this->queryLogFile, $log, FILE_APPEND);
|
||||
}
|
||||
if (!$this->link)
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
$sql = implode('; ', $queries);
|
||||
$r = $this->link->multi_query($sql);
|
||||
if (!$r)
|
||||
{
|
||||
if ($this->link->errno == 2006 && $this->reconnect && !$this->transactions)
|
||||
{
|
||||
// "MySQL server has gone away"
|
||||
$this->connect();
|
||||
$r = $this->link->multi_query($sql);
|
||||
if (!$r && $this->link->errno == 2006)
|
||||
{
|
||||
$this->link = false;
|
||||
}
|
||||
}
|
||||
if (!$r)
|
||||
{
|
||||
throw new DatabaseException($this->link->error, $this->link->errno);
|
||||
}
|
||||
}
|
||||
$results = array();
|
||||
foreach ($queries as $k => $q)
|
||||
{
|
||||
$results[$k] = $this->fetch_all($r, $format);
|
||||
$this->link->next_result();
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a transaction, supports nested calls and savepoints.
|
||||
* @param boolean $savepoint Creates savepoints only this parameter is true.
|
||||
|
@ -220,6 +305,8 @@ class DatabaseMysql implements Database
|
|||
* 'conditional expression',
|
||||
* 'field_name' => 'value',
|
||||
* 'field_name' => array('one', 'of', 'values'),
|
||||
* 'field_name < ?' => 'value',
|
||||
* 'field_name < DATE_SUB(?, ?)' => array('arg1', 'arg2'),
|
||||
* 'field1,field2' => array(array(1, 2), array(3, 4)),
|
||||
* )
|
||||
*/
|
||||
|
@ -231,7 +318,21 @@ class DatabaseMysql implements Database
|
|||
foreach ($where as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$wh[] = $v;
|
||||
{
|
||||
if (is_array($v))
|
||||
{
|
||||
$str = array_shift($v);
|
||||
$wh[] = $this->quoteInto($str, $v);
|
||||
}
|
||||
else
|
||||
{
|
||||
$wh[] = $v;
|
||||
}
|
||||
}
|
||||
elseif (($p = strrpos($k, '?')) !== false)
|
||||
{
|
||||
$wh[] = $this->quoteInto($k, (array)$v);
|
||||
}
|
||||
elseif (is_array($v))
|
||||
{
|
||||
if (!$v)
|
||||
|
@ -248,6 +349,8 @@ class DatabaseMysql implements Database
|
|||
$wh[] = "$k IN (" . implode(",", $v) . ")";
|
||||
}
|
||||
}
|
||||
elseif (preg_match('/^-?\d+(\.\d+)?$/s', $v)) // int/float
|
||||
$wh[] = "$k=$v";
|
||||
elseif ($v !== NULL)
|
||||
$wh[] = "$k=".$this->quote($v);
|
||||
else
|
||||
|
@ -255,6 +358,11 @@ class DatabaseMysql implements Database
|
|||
}
|
||||
if (!$wh)
|
||||
$where = '1';
|
||||
elseif (!$this->username && !$this->password && !$this->dbname)
|
||||
{
|
||||
// Sphinx supports neither brackets nor OR operator as of 2.0.6-release O_o
|
||||
$where = join(' AND ', $wh);
|
||||
}
|
||||
else
|
||||
$where = '(' . join(') AND (', $wh) . ')';
|
||||
return $where;
|
||||
|
@ -277,6 +385,9 @@ class DatabaseMysql implements Database
|
|||
* 'ORDER BY' => array($orderby_field1 => 'ASC', $orderby_field2 => 'DESC')
|
||||
* 'LIMIT' => array($limit, $offset) or array($limit) or just $limit
|
||||
* 'OFFSET' => $offset, for the case when 'LIMIT' is just $limit
|
||||
*
|
||||
* Sphinx Search extensions:
|
||||
* 'WITHIN GROUP ORDER BY' => array($orderby_field => 'ASC')
|
||||
*/
|
||||
function select_builder($tables, $fields, $where, $options = NULL)
|
||||
{
|
||||
|
@ -307,37 +418,16 @@ class DatabaseMysql implements Database
|
|||
$sql .= "$fields FROM $tables WHERE $where";
|
||||
if (isset($options['GROUP BY']))
|
||||
{
|
||||
$g = $options['GROUP BY'];
|
||||
if (is_array($g))
|
||||
{
|
||||
$g1 = array();
|
||||
foreach ($g as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$g1[] = $v;
|
||||
else
|
||||
$g1[] = "$k $v";
|
||||
}
|
||||
$g = join(',', $g1);
|
||||
}
|
||||
$sql .= " GROUP BY $g";
|
||||
$sql .= " GROUP BY ".$this->order_option($options['GROUP BY']);
|
||||
}
|
||||
if (isset($options['ORDER BY']))
|
||||
{
|
||||
$g = $options['ORDER BY'];
|
||||
if (is_array($g))
|
||||
{
|
||||
$g1 = array();
|
||||
foreach ($g as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$g1[] = $v;
|
||||
else
|
||||
$g1[] = "$k $v";
|
||||
}
|
||||
$g = join(',', $g1);
|
||||
}
|
||||
$sql .= " ORDER BY $g";
|
||||
$sql .= " ORDER BY ".$this->order_option($options['ORDER BY']);
|
||||
}
|
||||
if (isset($options['WITHIN GROUP ORDER BY']))
|
||||
{
|
||||
// Sphinx Search extension
|
||||
$sql .= " WITHIN GROUP ORDER BY ".$this->order_option($options['WITHIN GROUP ORDER BY']);
|
||||
}
|
||||
$sql .= $this->limit_option($options);
|
||||
if (isset($options['FOR UPDATE']))
|
||||
|
@ -347,10 +437,30 @@ class DatabaseMysql implements Database
|
|||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ORDER BY or GROUP BY options
|
||||
*/
|
||||
protected function order_option($g)
|
||||
{
|
||||
if (is_array($g))
|
||||
{
|
||||
$g1 = array();
|
||||
foreach ($g as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$g1[] = $v;
|
||||
else
|
||||
$g1[] = "$k $v";
|
||||
}
|
||||
$g = join(',', $g1);
|
||||
}
|
||||
return $g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a single LIMIT or LIMIT and OFFSET options.
|
||||
*/
|
||||
function limit_option($options)
|
||||
protected function limit_option($options)
|
||||
{
|
||||
if (isset($options['LIMIT']))
|
||||
{
|
||||
|
@ -457,10 +567,16 @@ class DatabaseMysql implements Database
|
|||
$sql = $this->select_builder($tables, $fields, $where, $options);
|
||||
if ($format & MS_RESULT)
|
||||
return $this->query($sql, MYSQLI_USE_RESULT);
|
||||
$res = $this->query($sql);
|
||||
return $this->fetch_all($res, $format);
|
||||
}
|
||||
|
||||
function fetch_all($res, $format = 0)
|
||||
{
|
||||
if ($format & MS_LIST)
|
||||
$r = $this->get_rows($sql);
|
||||
$r = $this->get_rows($res);
|
||||
else
|
||||
$r = $this->get_assocs($sql);
|
||||
$r = $this->get_assocs($res);
|
||||
if (!$r)
|
||||
$r = array();
|
||||
if ($format & MS_ROW)
|
||||
|
@ -478,6 +594,7 @@ class DatabaseMysql implements Database
|
|||
}
|
||||
elseif ($format & MS_COL)
|
||||
{
|
||||
$k = false;
|
||||
foreach ($r as $i => $v)
|
||||
{
|
||||
if (!$k)
|
||||
|
@ -518,8 +635,9 @@ class DatabaseMysql implements Database
|
|||
* Builds an INSERT query.
|
||||
* @param string $table Table name to insert rows to.
|
||||
* @param array $rows Array of table rows to be inserted.
|
||||
* @param boolean $onduplicatekey If true, include
|
||||
* ON DUPLICATE KEY UPDATE column=VALUES(column) for all columns.
|
||||
* @param boolean $onduplicatekey If true, create MySQL-specific "UPSERT" query using
|
||||
* INSERT .. ON DUPLICATE KEY UPDATE column=VALUES(column) for all columns.
|
||||
* @param boolean $replace If true, use REPLACE instead of INSERT
|
||||
*/
|
||||
function insert_builder($table, $rows, $onduplicatekey = false, $replace = false)
|
||||
{
|
||||
|
@ -592,7 +710,7 @@ class DatabaseMysql implements Database
|
|||
return false;
|
||||
if (!is_array(@$rows[0]))
|
||||
$rows = array($rows);
|
||||
$sql = $this->insert_builder($table, $rows, true, !empty($options['replace']));
|
||||
$sql = $this->insert_builder($table, $rows, empty($options['REPLACE']), !empty($options['REPLACE']));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -615,10 +733,16 @@ class DatabaseMysql implements Database
|
|||
return false;
|
||||
}
|
||||
|
||||
function get_rows($sql)
|
||||
function replace($table, $rows, $where = NULL, $options = array())
|
||||
{
|
||||
$options['REPLACE'] = true;
|
||||
return $this->update($table, $rows, $where, $options);
|
||||
}
|
||||
|
||||
protected function get_rows($res)
|
||||
{
|
||||
$r = array();
|
||||
if ($res = $this->query($sql))
|
||||
if ($res)
|
||||
{
|
||||
while ($row = $res->fetch_row())
|
||||
$r[] = $row;
|
||||
|
@ -627,10 +751,10 @@ class DatabaseMysql implements Database
|
|||
return $r;
|
||||
}
|
||||
|
||||
function get_assocs($sql)
|
||||
protected function get_assocs($res)
|
||||
{
|
||||
$r = array();
|
||||
if ($res = $this->query($sql))
|
||||
if ($res)
|
||||
{
|
||||
while ($row = $res->fetch_assoc())
|
||||
$r[] = $row;
|
||||
|
|
Loading…
Reference in New Issue